- Notifications
You must be signed in to change notification settings - Fork142
🍜 A tasty Haskell front-end framework
License
dmjio/miso
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
AtastyHaskell front-end web framework
Miso is a small, production-ready, component-oriented,isomorphicHaskell front-end framework for quickly building highly interactive single-page web applications. It features a virtual-dom, recursive diffing / patching algorithm, attribute and property normalization, event delegation, event batching, SVG, Server-sent events, Websockets, type-safeservant-style routing and an extensible Subscription-based subsystem. Inspired byElm,Redux andBobril.Miso is pure by default, but side effects (likeXHR
) can be introduced into the system via theEffect
data type.Miso makes heavy use of theGHC Javascript FFI and therefore has minimal dependencies.Miso can be considered a shallowembedded domain-specific language for modern web programming.
- Quick Start
- Examples
- Haddocks
- Sample Application
- Transition Application
- Live reload with JSaddle
- Docker
- Building examples
- Coverage
- Isomorphic
- Pinning nixpkgs
- Binary cache
- Benchmarks
- Maintainers
- Commercial Users
- Contributing
- Contributors
- License
To get started quickly building applications, we recommend using thenix
package manager with miso's binary cache provided bycachix
. It is possible to usestack
to build GHCJS projects, but support for procuringGHCJS
has been removedas of stack 2.0.nix
is used to procure a working version ofGHCJS
. If you're usingcabal
we assume you haveobtainedGHCJS
by other means. All source code depicted below for the quick start app is availablehere.
To build the sample-app withnix
, execute the commands below:
git clone https://github.com/dmjio/misocd miso/sample-appcabal build# now open http://localhost:8008 in your browser and you should see the +/- app
The above commands will add miso's binary cache to your nix installation (support for both Linux and OSX).nix-build
will fetch the dependencies from miso's cache and build the sample application.
Nix
is a more powerful option for building web applications withmiso
since it encompasses development workflow, configuration management, and deployment. The source code forhaskell-miso.org
is an example of this.
If unfamiliar withnix
, we recommend@Gabriella439's"Nix and Haskell in production" guide.
To begin, make the following directory layout:
➜ mkdir app&& touch app/{Main.hs,app.cabal,default.nix}&& tree appapp|-- Main.hs|-- app.cabal`-- default.nix
Add acabal
file
➜ cat app/*.cabalname: appversion: 0.1.0.0synopsis: First miso appcategory: Webbuild-type: Simplecabal-version:>=1.10executable app main-is: Main.hs ghcjs-options: -dedupe build-depends: base, miso default-language: Haskell2010
Write adefault.nix
(this will fetch a recent version ofmiso
).miso
will provide you with a workingnixpkgs
namedpkgs
.callCabal2nix
will automatically produce a nix expression that builds your cabal file.
with(import(builtins.fetchGit{url="https://github.com/dmjio/miso";ref="refs/tags/1.8";}){});pkgs.haskell.packages.ghcjs.callCabal2nix"app"./.{}
Add the source fromSample Application toapp/Main.hs
Build the project
nix-build
Open the result
open ./result/bin/app.jsexe/index.html
For development withnix
, it can be nice to havecabal
present for building. This command will make it available in yourPATH
.
nix-env -iA cabal-install -f '<nixpkgs>'
To be put into a shell w/GHCJS
and all the dependencies for this project present, usenix-shell
.
nix-shell -A env
To view the dependencies for your project, callghcjs-pkg list
when inside the shell.
nix-shell -A env --run 'ghcjs-pkg list'
To build the project withcabal
after entering thenix-shell
nix-shell -A env --run 'cabal configure --ghcjs && cabal build'
For incremental development inside of thenix-shell
we recommend using a tool likeentr
to automatically rebuild on file changes, or roll your own solution withinotify
.
ag -l | entr sh -c 'cabal build'
For constructing client and server applications, we recommend using onecabal
file with two executable sections, where thebuildable
attribute set is contingent on the compiler. An example of this layout ishere. For more info on how to usestack
with aclient
/server
setup, see thislink. For more information on how to usenix
with aclient
/server
setup, see thenix scripts forhttps://haskell-miso.org.
For details of the internals and general overview of howmiso
works, see theInternals.
----------------------------------------------------------------------------{-#LANGUAGE OverloadedStrings #-}{-#LANGUAGE CPP #-}----------------------------------------------------------------------------moduleMainwhere------------------------------------------------------------------------------| Miso framework importimportMisoimportMiso.String------------------------------------------------------------------------------| Type synonym for an application modeltypeModel=Int------------------------------------------------------------------------------| Sum type for application eventsdataAction=AddOne |SubtractOne |NoOp |SayHelloWorldderiving (Show,Eq)------------------------------------------------------------------------------| Entry point for a miso applicationmain::IO()main= run (startApp app)----------------------------------------------------------------------------app::AppModelActionapp= defaultApp emptyModel updateModel viewModelSayHelloWorld------------------------------------------------------------------------------| Empty modelemptyModel::ModelemptyModel=0------------------------------------------------------------------------------| Updates model, optionally introduces side effectsupdateModel::Action->Model->EffectActionModelupdateModelNoOp m= noEff mupdateModelAddOne m= noEff (m+1)updateModelSubtractOne m= noEff (m-1)updateModelSayHelloWorld m= m<#NoOp<$ consoleLog"Hello World"------------------------------------------------------------------------------| Constructs a virtual DOM from a modelviewModel::Model->ViewActionviewModel x= div_[] [ button_ [ onClickAddOne ] [ text"+" ] , text (ms x) , button_ [ onClickSubtractOne ] [ text"-" ] ]----------------------------------------------------------------------------
An alternative, more powerful interface for constructingmiso
applications is using theTransition
interface.Transition
is based on theStateT
monad transformer, and can be used to construct components. It also worksvery nicely with lenses based onMonadState
(i.e.(.=)
,(%=)
,(+=)
,(-=)
).
----------------------------------------------------------------------------{-#LANGUAGE OverloadedStrings #-}{-#LANGUAGE RecordWildCards #-}{-#LANGUAGE LambdaCase #-}----------------------------------------------------------------------------moduleMainwhere----------------------------------------------------------------------------importMisoimportMiso.String----------------------------------------------------------------------------importControl.Lens------------------------------------------------------------------------------| Application model statedataModel=Model{_counter::Int}deriving (Show,Eq)----------------------------------------------------------------------------counter::Lens'ModelIntcounter= lens _counter$\record field-> record { _counter= field }------------------------------------------------------------------------------| Sum type for App eventsdataAction=AddOne |SubtractOne |NoOp |SayHelloWorldderiving (Show,Eq)------------------------------------------------------------------------------| Entry point for a miso applicationmain::IO()main= run (startApp app)------------------------------------------------------------------------------| `defaultApp` takes as arguments the initial model, update function, view function and initial action.app::AppModelActionapp= defaultApp emptyModel (fromTransition. updateModel) viewModelSayHelloWorld------------------------------------------------------------------------------| Empty application stateemptyModel::ModelemptyModel=Model0------------------------------------------------------------------------------| Updates model, optionally introduces side effectsupdateModel::Action->TransitionActionModel()updateModel=\caseNoOp->pure()AddOne-> counter+=1SubtractOne-> counter-=1SayHelloWorld-> scheduleIO_ (consoleLog"Hello World")------------------------------------------------------------------------------| Constructs a virtual DOM from a modelviewModel::Model->ViewActionviewModel x= div_[] [ button_ [ onClickAddOne ] [ text"+" ] , text. ms$ x^.counter , button_ [ onClickSubtractOne ] [ text"-" ] ]----------------------------------------------------------------------------
It is possible to buildmiso
applications withghcid
,jsaddle
that allow live reloading of your application in reponse to changes in application code. To accomplish this, run thesample-app/
withghcid -c 'cabal repl app' -T=Main.main
. Then open your browser to http:localhost:8080. Whenever you edit the code, you should see the browser page refresh with an updated page.
Developing miso applications inside a Docker container is supported (allows applications to be built on Windows). See theREADME in thedocker
folder for more information.
The easiest way to build the examples is with thenix
package manager
git clone https://github.com/dmjio/miso && cd miso && nix-build --arg examples true
This will build all examples and documentation into a folder namedresult
➜ miso git:(master) ✗ tree -d ./result/bin./result/bin|-- canvas2d.jsexe|-- compose-update.jsexe|-- file-reader.jsexe|-- mario.jsexe| `-- imgs|-- mathml.jsexe|-- router.jsexe|-- simple.jsexe|-- svg.jsexe|-- tests.jsexe|-- threejs.jsexe|-- todo-mvc.jsexe|-- websocket.jsexe`-- xhr.jsexe
To see examples, we recommend hosting them with a webserver
cd result/bin/todo-mvc.jsexe && nix-shell -p python --run 'python -m SimpleHTTPServer'Serving HTTP on 0.0.0.0 port 8000 ...
The core algorithmic component of miso isdiff.js. It is responsible for all DOM manipulation that occurs in a miso application and has100% code coverage. Tests and coverage made possible usingjsdom andjest.
To run the tests and build the coverage report:
cd miso/testsnpm inpm runtest## Or by using `yarn` instead of `npm`:# yarn# yarn test
Isomorphic javascript is a technique for increased SEO, code-sharing and perceived page load times. It works in two parts. First, the server sends a pre-rendered HTML body to the client's browser. Second, after the client javascript application loads, the pointers of the pre-rendered DOM are copied into the virtual DOM, and the application proceeds as normal. All subsequent page navigation is handled locally by the client, avoiding full-page postbacks as necessary.
Themiso
function is used to perform the pointer-copying behavior client-side.
For more information on howmiso
handles isomorphic javascript, we recommendthis tutorial.
By defaultmiso
uses a known-to-work, pinned version ofnixpkgs
.
nix
users on a Linux or OSX distro can take advantage of abinary cache for faster builds. To use the binary cache follow the instructions oncachix.
cachix use miso-haskell
According to benchmarks,miso
is among the fastest functional programming web frameworks, second only toElm.
Feel free to dive in!Open an issue or submitPRs.
SeeCONTRIBUTING for more info.
This project exists thanks to all the people who contribute. [Contribute].
Become a financial contributor and help us sustain our community. [Contribute]
Support this project with your organization. Your logo will show up here with a link to your website. [Contribute]
BSD3 © David Johnson
About
🍜 A tasty Haskell front-end framework