packages feed

web-rep 0.7.2 → 0.8.0

raw patch · 19 files changed

+298/−638 lines, 19 filesdep +bifunctorsdep −doctestdep −network-simpledep −numhaskdep ~attoparsecdep ~boxdep ~box-socketsetup-changednew-component:exe:web-rep-example

Dependencies added: bifunctors

Dependencies removed: doctest, network-simple, numhask, tasty, tasty-hspec

Dependency ranges changed: attoparsec, box, box-socket, clay, concurrency, generic-lens, interpolatedstring-perl6, lens, lucid, mtl, text, transformers, unordered-containers, wai-middleware-static, wai-websockets, websockets

Files

+ ChangeLog.md view
@@ -0,0 +1,4 @@+0.8.0+===++* Removed numhask dependencies
Setup.hs view
@@ -1,2 +1,3 @@ import Distribution.Simple+ main = defaultMain
app/rep-example.hs view
@@ -3,16 +3,15 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeOperators #-} {-# OPTIONS_GHC -Wall #-} -import NumHask.Prelude import Options.Generic import Web.Rep import Web.Rep.Examples+import Prelude  data AppType = SharedTest deriving (Eq, Read, Show, Generic) @@ -22,10 +21,9 @@  instance ParseFields AppType -data Opts w-  = Opts-      { apptype :: w ::: AppType <?> "type of example"-      }+newtype Opts w = Opts+  { apptype :: w ::: AppType <?> "type of example"+  }   deriving (Generic)  instance ParseRecord (Opts Wrapped)@@ -34,4 +32,4 @@ main = do   o :: Opts Unwrapped <- unwrapRecord "examples for web-page"   case apptype o of-      SharedTest -> defaultSharedServer (maybeRep (Just "maybe") False repExamples)+    SharedTest -> defaultSharedServer (maybeRep (Just "maybe") False repExamples)
− readme.md
@@ -1,27 +0,0 @@-[web-rep](https://github.com/tonyday567/web-rep) [![Build Status](https://travis-ci.org/tonyday567/web-rep.svg)](https://travis-ci.org/tonyday567/web-rep)-===--Various functions and representations for a web page.--The best way to understand functionality is via running the example app:--```-stack build --test --exec "$(stack path --local-install-root)/bin/page-example --apptype SharedTest" --file-watch-```--http://localhost:9160/--reference------- [scotty](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/flags.html#flag-reference)-- get [bootstrap](https://getbootstrap.com/)-- [bootstrap-slider](https://seiyria.com/bootstrap-slider)-- [blaze](http://hackage.haskell.org/package/blaze-html)-- [lucid](http://hackage.haskell.org/package/lucid)-- [clay](https://www.stackage.org/clay)--todo------https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval#Do_not_ever_use_eval!
src/Web/Rep.hs view
@@ -51,7 +51,6 @@     module Web.Rep.Html,     module Web.Rep.Html.Input,     module Web.Rep.Bootstrap,-    module Web.Rep.Mathjax,      -- * re-exports     HashMap.HashMap,@@ -60,12 +59,11 @@  import qualified Data.HashMap.Strict as HashMap import Web.Rep.Bootstrap-import Web.Rep.Socket import Web.Rep.Html import Web.Rep.Html.Input-import Web.Rep.Mathjax import Web.Rep.Page import Web.Rep.Render import Web.Rep.Server import Web.Rep.Shared import Web.Rep.SharedReps+import Web.Rep.Socket
src/Web/Rep/Bootstrap.hs view
@@ -1,7 +1,6 @@ {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE StrictData #-} {-# OPTIONS_GHC -Wall #-}  -- | Some <https://getbootstrap.com/ bootstrap> assets and functionality.@@ -17,9 +16,12 @@   ) where +import Control.Monad.State.Lazy+import Data.Bool+import Data.Functor.Identity+import Data.Text (Text) import Lucid import Lucid.Base-import NumHask.Prelude import Web.Rep.Html import Web.Rep.Page import Web.Rep.Shared
src/Web/Rep/Examples.hs view
@@ -4,13 +4,12 @@ {-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-}-{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE StrictData #-} {-# OPTIONS_GHC -Wall #-}  module Web.Rep.Examples   ( page1,     page2,-    pagemj,     cfg2,     RepExamples (..),     repExamples,@@ -30,48 +29,40 @@ import qualified Clay import Control.Lens hiding ((.=)) import Data.Attoparsec.Text+import Data.Biapplicative+import Data.Bool+import Data.Text (Text, pack)+import GHC.Generics import Lucid-import NumHask.Prelude import Text.InterpolatedString.Perl6 import Web.Rep  -- | simple page example page1 :: Page page1 =-  #htmlBody .~ button1-    $ #cssBody .~ RepCss css1-    $ #jsGlobal .~ mempty-    $ #jsOnLoad .~ click-    $ #libsCss .~ (libCss <$> cssLibs)-    $ #libsJs .~ (libJs <$> jsLibs)-    $ mempty+  #htmlBody .~ button1 $+    #cssBody .~ RepCss css1 $+      #jsGlobal .~ mempty $+        #jsOnLoad .~ click $+          #libsCss .~ (libCss <$> cssLibs) $+            #libsJs .~ (libJs <$> jsLibs) $+              mempty  -- | page with localised libraries page2 :: Page page2 =-  #libsCss .~ (libCss <$> cssLibsLocal)-    $ #libsJs .~ (libJs <$> jsLibsLocal)-    $ page1---- | simple mathjax formulae-pagemj :: Page-pagemj = mathjaxPage & #htmlBody .~ htmlMathjaxExample--htmlMathjaxExample :: HtmlT Identity ()-htmlMathjaxExample =-  p_ "double dollar:"-    <> p_ "$$\\sum_{i=0}^n i^2 = \\frac{(n^2+n)(2n+1)}{6}$$"-    <> p_ "single dollar for inline: $\\sum_{i=0}^n i^2 = \\frac{(n^2+n)(2n+1)}{6}$"-    <> p_ "escaped brackets for inline mathjax: \\(\\sum_{i=0}^n i^2 = \\frac{(n^2+n)(2n+1)}{6}\\)"+  #libsCss .~ (libCss <$> cssLibsLocal) $+    #libsJs .~ (libJs <$> jsLibsLocal) $+      page1  cfg2 :: PageConfig cfg2 =-  #concerns .~ Separated-    $ #pageRender .~ Pretty-    $ #structure .~ Headless-    $ #localdirs .~ ["test/static"]-    $ #filenames .~ (("other/cfg2" <>) <$> suffixes)-    $ defaultPageConfig ""+  #concerns .~ Separated $+    #pageRender .~ Pretty $+      #structure .~ Headless $+        #localdirs .~ ["test/static"] $+          #filenames .~ (("other/cfg2" <>) <$> suffixes) $+            defaultPageConfig ""  cssLibs :: [Text] cssLibs =@@ -114,19 +105,18 @@     ("Go " <> with i_ [class__ "fa fa-play"] mempty)  -- | One of each sharedrep input instances.-data RepExamples-  = RepExamples-      { repTextbox :: Text,-        repTextarea :: Text,-        repSliderI :: Int,-        repSlider :: Double,-        repCheckbox :: Bool,-        repToggle :: Bool,-        repDropdown :: Int,-        repDropdownMultiple :: [Int],-        repShape :: Shape,-        repColor :: Text-      }+data RepExamples = RepExamples+  { repTextbox :: Text,+    repTextarea :: Text,+    repSliderI :: Int,+    repSlider :: Double,+    repCheckbox :: Bool,+    repToggle :: Bool,+    repDropdown :: Int,+    repDropdownMultiple :: [Int],+    repShape :: Shape,+    repColor :: Text+  }   deriving (Show, Eq, Generic)  -- | For a typed dropdown example.@@ -237,10 +227,10 @@         _ -> SumOnly     defInt = case defst of       SumInt i -> i-      _ -> defi+      _NotSumInt -> defi     defText = case defst of       SumText t -> t-      _ -> deft+      _NotSumText -> deft  data SumType2Example = SumOutside Int | SumInside SumTypeExample deriving (Eq, Show, Generic) @@ -287,7 +277,7 @@       case repst2' of         "SumOutside" -> SumOutside repoi'         "SumInside" -> SumInside repst'-        _ -> SumOutside repoi'+        _WeirdSpelling -> SumOutside repoi'     defInt = case defst of       SumInt i -> i-      _ -> defi+      _NotSumInt -> defi
src/Web/Rep/Html.hs view
@@ -1,7 +1,8 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE IncoherentInstances #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE StrictData #-}+{-# OPTIONS_GHC -Wall #-} {-# OPTIONS_GHC -fno-warn-orphans #-}  -- | Key generators and miscellaneous html utilities.@@ -17,10 +18,14 @@   ) where +import Data.Bool import Data.List (intersperse)+import Data.Text (Text, pack) import qualified Data.Text.Lazy as Lazy import Lucid-import NumHask.Prelude++-- $setup+-- >>> :set -XOverloadedStrings  -- | FIXME: A horrible hack to separate class id's class__ :: Text -> Attribute
src/Web/Rep/Html/Input.hs view
@@ -1,7 +1,6 @@ {-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE StrictData #-} {-# OPTIONS_GHC -Wall #-}  -- | Common web page input elements, often with bootstrap scaffolding.@@ -11,24 +10,25 @@   ) where -import Data.Text (split)+import Data.Bool+import Data.Maybe+import Data.Text (Text, pack, split)+import GHC.Generics import Lucid import Lucid.Base-import NumHask.Prelude hiding (for_) import Web.Rep.Html  -- | something that might exist on a web page and be a front-end input to computations.-data Input a-  = Input-      { -- | underlying value-        inputVal :: a,-        -- | label suggestion-        inputLabel :: Maybe Text,-        -- | name//key//id of the Input-        inputId :: Text,-        -- | type of html input-        inputType :: InputType-      }+data Input a = Input+  { -- | underlying value+    inputVal :: a,+    -- | label suggestion+    inputLabel :: Maybe Text,+    -- | name//key//id of the Input+    inputId :: Text,+    -- | type of html input+    inputType :: InputType+  }   deriving (Eq, Show, Generic)  -- | Various types of web page inputs, encapsulating practical bootstrap class functionality
− src/Web/Rep/Mathjax.hs
@@ -1,156 +0,0 @@-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE OverloadedLabels #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE QuasiQuotes #-}-{-# LANGUAGE NoImplicitPrelude #-}-{-# OPTIONS_GHC -Wall #-}---- | mathjax functionality.------ some <https://docs.mathjax.org/en/latest/web/start.html mathjax> assets------ <https://math.meta.stackexchange.com/questions/5020/mathjax-basic-tutorial-and-quick-reference mathjax cheatsheet>------ FIXME: Mathjax inside svg doesn't quite work, and needs to be structured around the foreign object construct.-module Web.Rep.Mathjax-  ( mathjaxPage,-    mathjax27Page,-    mathjaxSvgPage,-    mathjax27SvgPage,-  )-where--import Control.Lens-import Lucid-import NumHask.Prelude-import Text.InterpolatedString.Perl6-import Web.Rep.Page--mathjax3Lib :: Html ()-mathjax3Lib =-  with-    (script_ mempty)-    [ src_ "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-svg.js",-      id_ "MathJax-script",-      async_ ""-    ]--mathjax27Lib :: Html ()-mathjax27Lib =-  with-    (script_ mempty)-    [ src_ "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-MML-AM_SVG"-    ]--jqueryLib :: Html ()-jqueryLib =-  with-    (script_ mempty)-    [ src_ "https://code.jquery.com/jquery-3.3.1.slim.min.js",-      integrity_ "sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo",-      crossorigin_ "anonymous"-    ]---- | A 'Page' that loads mathjax-mathjaxPage :: Page-mathjaxPage =-  mempty-    & #jsGlobal .~ RepJsText scriptMathjaxConfig-    & #libsJs-      .~ [ mathjax3Lib-         ]---- | A 'Page' that loads the mathjax 2.7 js library-mathjax27Page :: Page-mathjax27Page =-  mempty-    & #jsOnLoad .~ RepJsText scriptMathjaxConfig-    & #libsJs-      .~ [ mathjax27Lib-         ]---- | A 'Page' that tries to enable mathjax inside svg (which is tricky).-mathjaxSvgPage :: Text -> Page-mathjaxSvgPage cl =-  mempty-    & #jsGlobal .~ RepJsText (scriptMathjaxConfigSvg cl)-    & #libsJs-      .~ [ mathjax3Lib,-           jqueryLib-         ]---- | A 'Page' that tries to enable mathjax 2.7 inside svg.-mathjax27SvgPage :: Text -> Page-mathjax27SvgPage cl =-  mempty-    & #jsGlobal .~ RepJsText (scriptMathjax27ConfigSvg cl)-    & #libsJs-      .~ [ mathjax27Lib,-           jqueryLib-         ]--scriptMathjaxConfig :: Text-scriptMathjaxConfig =-  [q|-MathJax = {-  tex: {-    inlineMath: [ ['$','$'], ["\\(","\\)"] ]-  }-};-|]---- | Mathjax applies within an svg element as normal, but results in an svg element inside a text element which is not allowed, hence the extra scripting.--- http://bl.ocks.org/larsenmtl/86077bddc91c3de8d3db6a53216b2f47-scriptMathjax27ConfigSvg :: Text -> Text-scriptMathjax27ConfigSvg cl =-  [qq|-     setTimeout(() => \{--         MathJax.Hub.Config(\{-             tex2jax: \{-                 inlineMath: [ ['$','$'], ["\\(","\\)"] ],-                 processEscapes: true-             }-         });--         MathJax.Hub.Register.StartupHook("End", function() \{-             setTimeout(() => \{-                 $('.{cl}').each(function()\{-                     var m = $('text>span>svg');-                     m.remove();-                     $(this).append(m);-                 });--             }, 1);-         });-     }, 1);-|]---- | Mathjax applies within an svg element as normal, but results in an svg element inside a text element which is not allowed, hence the extra scripting.--- http://bl.ocks.org/larsenmtl/86077bddc91c3de8d3db6a53216b2f47-scriptMathjaxConfigSvg :: Text -> Text-scriptMathjaxConfigSvg cl =-  [qq|-        window.MathJax = \{-             tex: \{-                 inlineMath: [ ['$','$'], ["\\(","\\)"] ]-             },-             startup: \{-                 ready: () => \{-                     MathJax.startup.defaultReady();-                     MathJax.startup.promise.then(() => \{-                         var xs = document.querySelectorAll('.{cl}');-                         xs.forEach((x) =>-                             \{ x.querySelectorAll('text').forEach((t) =>-                                 \{t.querySelectorAll('mjx-container>svg').forEach((s) =>-                                     \{-                                         Array.from(t.attributes).forEach((a) => s.setAttribute(a.name, a.value));-                                         x.appendChild(s);-                                 });-                             });-                         });-                     });-                 }-             }-         };-|]
src/Web/Rep/Page.hs view
@@ -1,10 +1,10 @@-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE DeriveFoldable #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DeriveTraversable #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE StrictData #-} {-# LANGUAGE TupleSections #-} {-# OPTIONS_GHC -fno-warn-incomplete-patterns #-} {-# OPTIONS_HADDOCK hide, not-home #-}@@ -20,13 +20,11 @@     PageConcerns (..),     PageStructure (..),     PageRender (..),-     -- $css     Css,     RepCss (..),     renderCss,     renderRepCss,-     -- $js     JS (..),     RepJs (..),@@ -34,41 +32,41 @@     renderRepJs,     parseJs,     renderJs,-    )+  ) where -import qualified Clay import Clay (Css)+import qualified Clay import Control.Lens import Data.Generics.Labels ()-import GHC.Show (show)+import Data.Text (Text, unpack)+import Data.Text.Lazy (toStrict)+import GHC.Generics import Language.JavaScript.Parser import Language.JavaScript.Parser.AST import Language.JavaScript.Process.Minify import Lucid-import NumHask.Prelude hiding (show) import Text.InterpolatedString.Perl6  -- | Components of a web page. -- -- A web page can take many forms but still have the same underlying representation. For example, CSS can be linked to in a separate file, or can be inline within html, but still be the same css and have the same expected external effect. A Page represents the practical components of what makes up a static snapshot of a web page.-data Page-  = Page-      { -- | css library links-        libsCss :: [Html ()],-        -- | javascript library links-        libsJs :: [Html ()],-        -- | css-        cssBody :: RepCss,-        -- | javascript with global scope-        jsGlobal :: RepJs,-        -- | javascript included within the onLoad function-        jsOnLoad :: RepJs,-        -- | html within the header-        htmlHeader :: Html (),-        -- | body html-        htmlBody :: Html ()-      }+data Page = Page+  { -- | css library links+    libsCss :: [Html ()],+    -- | javascript library links+    libsJs :: [Html ()],+    -- | css+    cssBody :: RepCss,+    -- | javascript with global scope+    jsGlobal :: RepJs,+    -- | javascript included within the onLoad function+    jsOnLoad :: RepJs,+    -- | html within the header+    htmlHeader :: Html (),+    -- | body html+    htmlBody :: Html ()+  }   deriving (Show, Generic)  instance Semigroup Page where@@ -90,12 +88,11 @@ -- | A web page typically is composed of some css, javascript and html. -- -- 'Concerns' abstracts this structural feature of a web page.-data Concerns a-  = Concerns-      { cssConcern :: a,-        jsConcern :: a,-        htmlConcern :: a-      }+data Concerns a = Concerns+  { cssConcern :: a,+    jsConcern :: a,+    htmlConcern :: a+  }   deriving (Eq, Show, Foldable, Traversable, Generic)  instance Functor Concerns where@@ -137,14 +134,13 @@   deriving (Show, Eq, Generic)  -- | Configuration options when rendering a 'Page'.-data PageConfig-  = PageConfig-      { concerns :: PageConcerns,-        structure :: PageStructure,-        pageRender :: PageRender,-        filenames :: Concerns FilePath,-        localdirs :: [FilePath]-      }+data PageConfig = PageConfig+  { concerns :: PageConcerns,+    structure :: PageStructure,+    pageRender :: PageRender,+    filenames :: Concerns FilePath,+    localdirs :: [FilePath]+  }   deriving (Show, Eq, Generic)  -- | Default configuration is inline ecma and css, separate html header and body, minified code, with the suggested filename prefix.@@ -186,7 +182,6 @@ -- | Render 'Css' as text. renderCss :: Css -> Text renderCss = toStrict . Clay.render-  -- | wrapper for `JSAST` newtype JS = JS {unJS :: JSAST} deriving (Show, Eq, Generic)
src/Web/Rep/Render.hs view
@@ -1,6 +1,5 @@ {-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE NoImplicitPrelude #-}  -- | Page rendering module Web.Rep.Render@@ -13,9 +12,12 @@   ) where +import Control.Applicative import Control.Lens+import Control.Monad+import Data.Foldable+import Data.Text (Text, pack, unpack) import Lucid-import NumHask.Prelude import Web.Rep.Html import Web.Rep.Page @@ -117,14 +119,14 @@ -- | Render Page concerns to files. renderPageToFile :: FilePath -> PageConfig -> Page -> IO () renderPageToFile dir pc page =-  sequenceA_ $ liftA2 writeFile' (pc ^. #filenames) (renderPageAsText pc page)+  sequenceA_ $ liftA2 writeFile' (pc ^. #filenames) (fmap unpack (renderPageAsText pc page))   where     writeFile' fp s = unless (s == mempty) (writeFile (dir <> "/" <> fp) s)  -- | Render a page to just a Html file. renderPageHtmlToFile :: FilePath -> PageConfig -> Page -> IO () renderPageHtmlToFile file pc page =-  writeFile file (toText $ renderPageHtmlWith pc page)+  writeFile file (unpack $ toText $ renderPageHtmlWith pc page)  -- | Render a Page as Text. renderPageAsText :: PageConfig -> Page -> Concerns Text
src/Web/Rep/Server.hs view
@@ -1,21 +1,21 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedLabels #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE NoImplicitPrelude #-} {-# OPTIONS_GHC -Wall #-}  -- | Serve pages via 'ScottyM' module Web.Rep.Server-  ( servePageWith+  ( servePageWith,   ) where  import Control.Lens hiding (only)+import Control.Monad+import Control.Monad.Trans.Class+import Data.Text (unpack) import Lucid import Network.Wai.Middleware.Static (addBase, noDots, only, staticPolicy)-import NumHask.Prelude hiding (get, replace)-import Web.Rep.Render import Web.Rep.Page+import Web.Rep.Render import Web.Scotty  -- | serve a Page via a ScottyM@@ -33,8 +33,8 @@               get                 rp                 ( do-                    lift $ writeFile' cssfp css-                    lift $ writeFile' jsfp js+                    lift $ writeFile' cssfp (unpack css)+                    lift $ writeFile' jsfp (unpack js)                     html $ renderText h                 )     cssfp = pc ^. #filenames . #cssConcern
src/Web/Rep/Shared.hs view
@@ -1,14 +1,13 @@-{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE DeriveTraversable #-}-{-# LANGUAGE OverloadedLabels #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE StrictData #-} {-# LANGUAGE TupleSections #-} {-# OPTIONS_GHC -fno-warn-incomplete-patterns #-} {-# OPTIONS_HADDOCK hide, not-home #-}  module Web.Rep.Shared-  (-    RepF (..),+  ( RepF (..),     Rep,     oneRep,     SharedRepF (..),@@ -22,23 +21,25 @@   ) where +import Control.Applicative import Control.Lens+import Control.Monad.IO.Class+import Control.Monad.State.Lazy+import Data.Biapplicative import Data.Generics.Labels ()-import qualified Data.HashMap.Strict as HashMap import Data.HashMap.Strict (HashMap)-import GHC.Show (show)+import qualified Data.HashMap.Strict as HashMap+import Data.Text (Text, pack) import Lucid-import NumHask.Prelude hiding (show)  -- | -- Information contained in a web page can usually be considered to be isomorphic to a map of named values - a 'HashMap'. This is especially true when considering a differential of information contained in a web page. Looking at a page from the outside, it often looks like a streaming differential of a hashmap. -- -- RepF consists of an underlying value being represented, and, given a hashmap state, a way to produce a representation of the underlying value (or error), in another domain, together with the potential to alter the hashmap state.-data RepF r a-  = Rep-      { rep :: r,-        make :: HashMap Text Text -> (HashMap Text Text, Either Text a)-      }+data RepF r a = Rep+  { rep :: r,+    make :: HashMap Text Text -> (HashMap Text Text, Either Text a)+  }   deriving (Functor)  -- | the common usage, where the representation domain is Html@@ -94,11 +95,9 @@ -- This is sometimes referred to as "observable sharing". See <http://hackage.haskell.org/package/data-reify data-reify> as another library that reifies this (pun intended), and provided the initial inspiration for this implementation. -- -- unshare should only be run once, which is a terrible flaw that might be fixed by linear types.----newtype SharedRepF m r a-  = SharedRep-      { unshare :: StateT (Int, HashMap Text Text) m (RepF r a)-      }+newtype SharedRepF m r a = SharedRep+  { unshare :: StateT (Int, HashMap Text Text) m (RepF r a)+  }   deriving (Functor)  -- | default representation type of 'Html' ()@@ -148,10 +147,10 @@         (f name a)         ( \s ->             ( s,-              join-                $ maybe (Left "lookup failed") Right-                $ either (Left . (\x -> name <> ": " <> x)) Right . p <$>-                HashMap.lookup name s+              join $+                maybe (Left "lookup failed") Right $+                  either (Left . (\x -> name <> ": " <> x)) Right . p+                    <$> HashMap.lookup name s             )         ) @@ -175,10 +174,10 @@         (f name a)         ( \s ->             ( HashMap.delete name s,-              join-                $ maybe (Right $ Right d) Right-                $ p <$>-                HashMap.lookup name s+              join $+                maybe (Right $ Right d) Right $+                  p+                    <$> HashMap.lookup name s             )         ) @@ -204,4 +203,3 @@   (Rep h fa, (_, m)) <- runSharedRep sr   action h m   pure (fa m)-
src/Web/Rep/SharedReps.hs view
@@ -3,10 +3,9 @@ {-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE StrictData #-} {-# LANGUAGE TupleSections #-}-{-# LANGUAGE NoImplicitPrelude #-} {-# OPTIONS_GHC -Wall #-}-{-# OPTIONS_GHC -Wredundant-constraints #-}  -- | Various SharedRep instances for common html input elements. module Web.Rep.SharedReps@@ -42,20 +41,23 @@  import Box.Cont () import Control.Lens+import Control.Monad+import Control.Monad.State.Lazy import Data.Attoparsec.Text hiding (take)+import qualified Data.Attoparsec.Text as A+import Data.Biapplicative+import Data.Bool import qualified Data.HashMap.Strict as HashMap import qualified Data.List as List-import Data.Text (intercalate)+import Data.Text (Text, intercalate, pack, unpack) import Lucid-import NumHask.Prelude hiding (intercalate, takeWhile) import Text.InterpolatedString.Perl6 import Web.Rep.Bootstrap import Web.Rep.Html import Web.Rep.Html.Input-import Web.Rep.Shared import Web.Rep.Page-import qualified Prelude as P-import qualified Data.Attoparsec.Text as A+import Web.Rep.Shared+import Prelude as P  -- $setup -- >>> :set -XOverloadedStrings@@ -72,8 +74,7 @@   -- | initial value   a ->   SharedRep m a-repInput p pr i a =-  register (first pack . A.parseOnly p) pr (\n v -> toHtml $ #inputVal .~ v $ #inputId .~ n $ i) a+repInput p pr i = register (first pack . A.parseOnly p) pr (\n v -> toHtml $ #inputVal .~ v $ #inputId .~ n $ i)  -- | Like 'repInput', but does not put a value into the HashMap on instantiation, consumes the value when found in the HashMap, and substitutes a default on lookup failure repMessage :: (Monad m, ToHtml a) => Parser a -> (a -> Text) -> Input a -> a -> a -> SharedRep m a@@ -84,7 +85,7 @@ -- -- For Example, a slider between 0 and 1 with a step of 0.01 and a default value of 0.3 is: ----- >>> :t slider (Just "label") 0 1 0.01 0.3+-- > :t slider (Just "label") 0 1 0.01 0.3 -- slider (Just "label") 0 1 0.01 0.3 :: Monad m => SharedRep m Double slider ::   (Monad m) =>@@ -105,7 +106,7 @@ -- -- For Example, a slider between 0 and 1000 with a step of 10 and a default value of 300 is: ----- >>> :t sliderI (Just "label") 0 1000 10 300+-- > :t sliderI (Just "label") 0 1000 10 300 -- sliderI (Just "label") 0 1000 10 300 --   :: (Monad m, ToHtml a, P.Integral a, Show a) => SharedRep m a sliderI ::@@ -125,7 +126,7 @@  -- | textbox classique ----- >>> :t textbox (Just "label") "some text"+-- > :t textbox (Just "label") "some text" -- textbox (Just "label") "some text" :: Monad m => SharedRep m Text textbox :: (Monad m) => Maybe Text -> Text -> SharedRep m Text textbox label v =@@ -304,10 +305,10 @@         (toHtml (Input v label name (Checkbox v)) <> scriptToggleShow name id')         ( \s ->             ( s,-              join-                $ maybe (Left "HashMap.lookup failed") Right-                $ either (Left . pack) Right . parseOnly ((== "true") <$> takeText)-                  <$> HashMap.lookup name s+              join $+                maybe (Left "HashMap.lookup failed") Right $+                  either (Left . pack) Right . parseOnly ((== "true") <$> takeText)+                    <$> HashMap.lookup name s             )         ) @@ -327,12 +328,12 @@ accordionList :: (Monad m) => Maybe Text -> Text -> Maybe Text -> (Text -> a -> SharedRep m a) -> [Text] -> [a] -> SharedRep m [a] accordionList title prefix open srf labels as = SharedRep $ do   (Rep h fa) <--    unshare-      $ first (accordion prefix open . zip labels)-      $ foldr-        (\a x -> bimap (:) (:) a <<*>> x)-        (pure [])-        (zipWith srf labels as)+    unshare $+      first (accordion prefix open . zip labels) $+        foldr+          (\a x -> bimap (:) (:) a <<*>> x)+          (pure [])+          (zipWith srf labels as)   h' <- zoom _1 h   pure (Rep (maybe mempty (h5_ . toHtml) title <> h') fa) @@ -340,21 +341,16 @@ accordionBoolList :: (Monad m) => Maybe Text -> Text -> (a -> SharedRep m a) -> (Bool -> SharedRep m Bool) -> [Text] -> [(Bool, a)] -> SharedRep m [(Bool, a)] accordionBoolList title prefix bodyf checkf labels xs = SharedRep $ do   (Rep h fa) <--    unshare-      $ first (accordionChecked prefix)-      $ first (zipWith (\l (ch, a) -> (l, a, ch)) labels)-      $ foldr-        (\a x -> bimap (:) (:) a <<*>> x)-        (pure [])-        ( ( \(ch, a) ->-              bimap-                  (,)-                  (,)-                  (checkf ch)-                  <<*>> bodyf a-          )-            <$> xs+    unshare $+      first+        ( accordionChecked prefix+            . zipWith (\l (ch, a) -> (l, a, ch)) labels         )+        ( foldr+            (\a x -> bimap (:) (:) a <<*>> x)+            (pure [])+            ((\(ch, a) -> bimap (,) (,) (checkf ch) <<*>> bodyf a) <$> xs)+        )   h' <- zoom _1 h   pure (Rep (maybe mempty (h5_ . toHtml) title <> h') fa) @@ -403,7 +399,7 @@     parsed str =       case reads str of         [(a, "")] -> Right a-        _ -> Left (pack str)+        _badRead -> Left (pack str)  -- | Representation of web concerns (css, js & html). fiddle :: (Monad m) => Concerns Text -> SharedRep m (Concerns Text, Bool)@@ -449,20 +445,20 @@     hmap dd' cs' =       div_         ( dd'-            <> mconcat (zipWith (\c t -> subtype c t0 t) cs' ts)+            <> mconcat (zipWith (`subtype` t0) cs' ts)         )     mmap dd' cs' = maybe (List.head cs') (cs' List.!!) (List.elemIndex dd' ts)  -- | select test keys from a Map-selectItems :: [Text] -> HashMap.HashMap Text a -> [(Text,a)]+selectItems :: [Text] -> HashMap.HashMap Text a -> [(Text, a)] selectItems ks m =   HashMap.toList $     HashMap.filterWithKey (\k _ -> k `elem` ks) m  -- | rep of multiple items list repItemsSelect :: Monad m => [Text] -> [Text] -> SharedRep m [Text]-repItemsSelect init full =-  dropdownMultiple (A.takeWhile (`notElem` ([',']::[Char]))) id (Just "items") full init+repItemsSelect initial full =+  dropdownMultiple (A.takeWhile (`notElem` ([','] :: [Char]))) id (Just "items") full initial  subtype :: With a => a -> Text -> Text -> a subtype h origt t =
src/Web/Rep/Socket.hs view
@@ -3,8 +3,8 @@ {-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE StrictData #-} {-# OPTIONS_GHC -Wall #-}-{-# OPTIONS_GHC -Wredundant-constraints #-}  -- | A socket between a web page and haskell, based on the box library. module Web.Rep.Socket@@ -12,44 +12,49 @@     serveSocketBox,     sharedServer,     defaultSharedServer,-    SocketConfig(..),+    SocketConfig (..),     defaultSocketConfig,     defaultSocketPage,     defaultInputCode,     defaultOutputCode,-    Code(..),+    Code (..),     code,     wrangle,   ) where -import qualified Network.WebSockets as WS import Box import Box.Socket import Control.Lens+import Control.Monad import Control.Monad.Conc.Class as C+import Control.Monad.State.Lazy+import qualified Data.Attoparsec.Text as A+import Data.Bifunctor import Data.HashMap.Strict as HashMap+import Data.Text (Text, pack) import qualified Data.Text as Text-import NumHask.Prelude hiding (intercalate, replace)+import GHC.Generics+import Lucid as L+import Network.Wai.Handler.WebSockets+import qualified Network.WebSockets as WS import Text.InterpolatedString.Perl6+import Web.Rep.Bootstrap import Web.Rep.Html import Web.Rep.Page import Web.Rep.Server import Web.Rep.Shared-import Web.Rep.Bootstrap import Web.Scotty hiding (get)-import qualified Data.Attoparsec.Text as A-import Network.Wai.Handler.WebSockets-import Lucid as L  socketPage :: Page-socketPage = mempty & #jsOnLoad .~-  mconcat-  [ webSocket,-    runScriptJs,-    refreshJsbJs,-    preventEnter-  ]+socketPage =+  mempty & #jsOnLoad+    .~ mconcat+      [ webSocket,+        runScriptJs,+        refreshJsbJs,+        preventEnter+      ]  serveSocketBox :: SocketConfig -> Page -> Box IO Text Text -> IO () serveSocketBox cfg p b =@@ -59,8 +64,8 @@  sharedServer :: SharedRep IO a -> SocketConfig -> Page -> (Html () -> [Code]) -> (Either Text a -> IO [Code]) -> IO () sharedServer srep cfg p i o =-  serveSocketBox cfg p <$.>-  fromAction (backendLoop srep i o . wrangle)+  serveSocketBox cfg p+    <$.> fromAction (backendLoop srep i o . wrangle)  defaultSharedServer :: (Show a) => SharedRep IO a -> IO () defaultSharedServer srep =@@ -68,18 +73,18 @@  defaultSocketPage :: Page defaultSocketPage =-  bootstrapPage <>-  socketPage &-  #htmlBody-      .~ divClass_-        "container"-        ( mconcat-            [ divClass_ "row" (h1_ "web-rep testing"),-              divClass_ "row" $ mconcat $ (\(t, h) -> divClass_ "col" (h2_ (toHtml t) <> L.with div_ [id_ t] h)) <$> sections-            ]-        )+  bootstrapPage+    <> socketPage+    & #htmlBody+    .~ divClass_+      "container"+      ( mconcat+          [ divClass_ "row" (h1_ "web-rep testing"),+            divClass_ "row" $ mconcat $ (\(t, h) -> divClass_ "col" (h2_ (toHtml t) <> L.with div_ [id_ t] h)) <$> sections+          ]+      )   where-    sections = +    sections =       [ ("input", mempty),         ("output", mempty)       ]@@ -92,7 +97,8 @@   (Html () -> [Code]) ->   -- | output code   (Either Text a -> m [Code]) ->-  Box m [Code] (Text, Text) -> m ()+  Box m [Code] (Text, Text) ->+  m () backendLoop sr inputCode outputCode (Box c e) = flip evalStateT (0, HashMap.empty) $ do   -- you only want to run unshare once for a SharedRep   (Rep h fa) <- unshare sr@@ -108,14 +114,13 @@       b <- lift $ commit c o       when b (go fa)     updateS Nothing s = s-    updateS (Just (k,v)) s = second (insert k v) s+    updateS (Just (k, v)) s = second (insert k v) s      step' fa = do       s <- get       let (m', ea) = fa (snd s)       modify (second (const m'))-      o <- lift $ outputCode ea-      pure o+      lift $ outputCode ea  defaultInputCode :: Html () -> [Code] defaultInputCode h = [Append "input" (toText h)]@@ -123,33 +128,35 @@ defaultOutputCode :: (Monad m, Show a) => Either Text a -> m [Code] defaultOutputCode ea =   pure $ case ea of-        Left err -> [Append "debug" err]-        Right a -> [Replace "output" (show a)]+    Left err -> [Append "debug" err]+    Right a -> [Replace "output" (pack $ show a)] -wrangle :: Monad m => Box m Text Text -> Box m [Code] (Text,Text)+wrangle :: Monad m => Box m Text Text -> Box m [Code] (Text, Text) wrangle (Box c e) = Box c' e'   where     c' = listC $ contramap code c     e' = mapE (pure . either (const Nothing) Just) (parseE parserJ e)  -- | {"event":{"element":"textid","value":"abcdees"}}-parserJ :: A.Parser (Text,Text)+parserJ :: A.Parser (Text, Text) parserJ = do   _ <- A.string [q|{"event":{"element":"|]-  e <- A.takeTill (=='"')+  e <- A.takeTill (== '"')   _ <- A.string [q|","value":"|]-  v <- A.takeTill (=='"')+  v <- A.takeTill (== '"')   _ <- A.string [q|"}}|]-  pure (e,v)+  pure (e, v)  -- * code hooks+ -- * code messaging-data Code =-  Replace Text Text |-  Append Text Text |-  Console Text |-  Eval Text |-  Val Text++data Code+  = Replace Text Text+  | Append Text Text+  | Console Text+  | Eval Text+  | Val Text   deriving (Eq, Show, Generic, Read)  code :: Code -> Text@@ -168,7 +175,7 @@ -- | replace a container and run any embedded scripts replace :: Text -> Text -> Text replace d t =-      [qc|+  [qc|      var $container = document.getElementById('{d}');      $container.innerHTML = '{clean t}';      runScripts($container);@@ -178,7 +185,7 @@ -- | append to a container and run any embedded scripts append :: Text -> Text -> Text append d t =-      [qc|+  [qc|      var $container = document.getElementById('{d}');      $container.innerHTML += '{clean t}';      runScripts($container);@@ -192,6 +199,7 @@     . Text.lines  -- * initial javascript+ -- | create a web socket for event data webSocket :: RepJs webSocket =@@ -207,6 +215,7 @@ |]  -- * scripts+ -- | Event hooks that may need to be reattached given dynamic content creation. refreshJsbJs :: RepJs refreshJsbJs =
− stack.yaml
@@ -1,17 +0,0 @@-resolver: nightly-2020-11-19--packages:-  - '.'--extra-deps:-  - numhask-0.7.0.0-  - protolude-0.3.0-  - random-1.2.0-  - splitmix-0.1.0.3-  - numhask-space-0.7.0.0-  - box-0.6.2-  - box-socket-0.1.2--allow-newer: true--
− test/test.hs
@@ -1,92 +0,0 @@-{-# LANGUAGE OverloadedLabels #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE NoImplicitPrelude #-}--module Main where--import Control.Lens-import Lucid-import NumHask.Prelude-import Test.DocTest-import Test.Tasty-import Test.Tasty.Hspec-import Web.Rep-import Web.Rep.Examples-import qualified Data.Text.IO as Text--generatePage :: FilePath -> FilePath -> PageConfig -> Page -> IO ()-generatePage dir stem pc =-  renderPageToFile dir (#filenames .~ concernNames "" stem $ pc) --generatePages ::-     Traversable t => FilePath -> t (FilePath, PageConfig, Page) -> IO ()-generatePages dir xs =-  sequenceA_ $ (\(fp, pc, p) -> generatePage dir fp pc p) <$> xs--genTest :: FilePath -> IO ()-genTest dir =-  void $ generatePages dir [("default", defaultPageConfig "default", page1), ("sep", cfg2, page2)]--testVsFile :: FilePath -> FilePath -> PageConfig -> Page -> IO Bool-testVsFile dir stem pc p = do-  (t,t') <- textVsFile dir stem pc p-  pure (t==t')--textVsFile-  :: FilePath-  -> FilePath-  -> PageConfig-  -> Page-  -> IO (Concerns Text, Concerns Text)-textVsFile dir stem pc p = do-  let names = concernNames "" stem-  let pc' = #filenames .~ names $ pc-  let t = renderPageAsText pc' p-  case pc ^. #concerns of-    Inline -> do-      t' <- Text.readFile (dir <> names ^. #htmlConcern)-      return (t, Concerns mempty mempty t')-    Separated -> do-      t' <- sequenceA $ Text.readFile . (dir <>) <$> names-      return (t, t')--testsRender :: IO (SpecWith ())-testsRender =-  return $-    describe "Web.Rep.Render" $ do-      it "run genTest 'test/canned/' to refresh canned files." True-      it "renderPage mempty" $-        renderText (renderPage mempty) `shouldBe`-        "<!DOCTYPE HTML><html lang=\"en\"><head><meta charset=\"utf-8\"></head><body><script>window.onload=function(){}</script></body></html>"-      let dir = "test/canned/"-      it "renderPageToFile, renderPage (compared with default canned file)" $-        testVsFile dir "default" (defaultPageConfig "default") page1 `shouldReturn` True-      it "the various PageConfig's" $-        testVsFile dir "sep" cfg2 page2 `shouldReturn` True--testsBootstrap :: IO (SpecWith ())-testsBootstrap =-  return $-    describe "Web.Rep.Bootstrap" $ do-      it "bootstrapPage versus canned" $-        toText (renderPage bootstrapPage) `shouldBe`-        "<!DOCTYPE HTML><html lang=\"en\"><head><meta charset=\"utf-8\"><link crossorigin=\"anonymous\" href=\"https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css\" integrity=\"sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T\" rel=\"stylesheet\"><meta charset=\"utf-8\"><meta content=\"width=device-width, initial-scale=1, shrink-to-fit=no\" name=\"viewport\"></head><body><script crossorigin=\"anonymous\" integrity=\"sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo\" src=\"https://code.jquery.com/jquery-3.3.1.slim.min.js\"></script><script crossorigin=\"anonymous\" integrity=\"sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1\" src=\"https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js\"></script><script crossorigin=\"anonymous\" integrity=\"sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM\" src=\"https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js\"></script><script>window.onload=function(){}</script></body></html>"-      it "accordion versus canned" $-        ( toText .-          runIdentity .-          flip evalStateT 0 .-          accordion "acctest" Nothing $-          (\x -> (pack (show x), "filler")) <$> [1..2::Int]) `shouldBe`-        "<div id=\"acctest1\" class=\" accordion m-1 \"><div class=\" card \"><div id=\"acctest2\" class=\" card-header p-0 \"><h2 class=\" m-0 \"><button data-toggle=\"collapse\" data-target=\"#acctest3\" aria-controls=\"acctest3\" type=\"button\" class=\" btn btn-link collapsed \" aria-expanded=\"false\">1</button></h2></div><div data-parent=\"#acctest1\" id=\"acctest3\" aria-labelledby=\"acctest2\" class=\" collapse \"><div class=\" card-body \">filler</div></div></div><div class=\" card \"><div id=\"acctest4\" class=\" card-header p-0 \"><h2 class=\" m-0 \"><button data-toggle=\"collapse\" data-target=\"#acctest5\" aria-controls=\"acctest5\" type=\"button\" class=\" btn btn-link collapsed \" aria-expanded=\"false\">2</button></h2></div><div data-parent=\"#acctest1\" id=\"acctest5\" aria-labelledby=\"acctest4\" class=\" collapse \"><div class=\" card-body \">filler</div></div></div></div>"---- The tests-tests :: IO TestTree-tests = testGroup "the tests" <$> sequence-  [ testSpec "Web.Rep.Render" =<< testsRender-  , testSpec "Web.Rep.Bootstrap" =<< testsBootstrap-  ]--main :: IO ()-main = do-  doctest ["src/Web/Rep/SharedReps.hs"]-  defaultMain =<< tests
web-rep.cabal view
@@ -1,18 +1,23 @@-cabal-version:  2.4-name:           web-rep-version:        0.7.2-synopsis:       representations of a web page-category: web-description:    An applicative-based, shared-data representation of a web page. -bug-reports:    https://github.com/tonyday567/web-page/issues-maintainer:     Tony Day <tonyday567@gmail.com>-license:        MIT-license-file:   LICENSE.md-build-type:     Simple-extra-source-files:-    stack.yaml-    readme.md+cabal-version:      2.4+name:               web-rep+version:            0.8.0+synopsis:           representations of a web page+category:           web+description:+  An applicative-based, shared-data representation of a web page.  +bug-reports:        https://github.com/tonyday567/web-page/issues+maintainer:         Tony Day <tonyday567@gmail.com>+license:            MIT+license-file:       LICENSE.md+build-type:         Simple+extra-source-files: ChangeLog.md+tested-with:        GHC ==8.8.4 || ==8.10.4 || ==9.0.1 || ==9.2.0.20210821++source-repository head+  type:     git+  location: https://github.com/tonyday567/web-rep+ library   exposed-modules:     Web.Rep@@ -20,106 +25,55 @@     Web.Rep.Examples     Web.Rep.Html     Web.Rep.Html.Input-    Web.Rep.Mathjax     Web.Rep.Page     Web.Rep.Render     Web.Rep.Server     Web.Rep.Shared     Web.Rep.SharedReps     Web.Rep.Socket-  hs-source-dirs:-    src-  build-depends:-    attoparsec >= 0.13.2 && < 0.14,-    base >= 4.12 && <5,-    box >= 0.6 && < 0.7,-    box-socket >= 0.0.2 && < 0.2,-    clay >= 0.13 && < 0.14,-    concurrency >= 1.11,-    generic-lens >= 1.1.0 && < 3.0,-    interpolatedstring-perl6 >= 1.0.2 && < 1.1,-    language-javascript >= 0.6.0 && < 0.8,-    lens >= 4.17.1 && < 4.20,-    lucid >= 2.9 && < 2.10,-    mtl >= 2.2.2 && < 2.3,-    network-simple >= 0.4.5 && < 0.4.6,-    numhask >= 0.7 && < 0.8,-    scotty >= 0.11.5 && < 0.13,-    text >= 1.2.3 && < 1.3,-    transformers >= 0.5.6 && < 0.6,-    unordered-containers >= 0.2.10 && < 0.3,-    wai-middleware-static >= 0.8.3,-    wai-websockets >= 3.0.1.2,-    websockets >= 0.12,-  default-language: Haskell2010-  default-extensions:-    NegativeLiterals-    NoImplicitPrelude-    OverloadedStrings-    UnicodeSyntax-  ghc-options:-    -Wall-    -Wcompat-    -Wincomplete-record-updates-    -Wincomplete-uni-patterns-    -Wredundant-constraints -executable rep-example-  main-is: rep-example.hs-  hs-source-dirs:-    app+  hs-source-dirs:     src   build-depends:-    base >= 4.12 && <5,-    numhask >= 0.7 && < 0.8,-    optparse-generic >= 1.3,-    web-rep,-  default-language: Haskell2010+    , attoparsec                ^>=0.14+    , base                      >=4.12    && <5+    , bifunctors                ^>=5.5.11+    , box                       ^>=0.7+    , box-socket                ^>=0.2+    , clay                      ^>=0.13+    , concurrency               ^>=1.11+    , generic-lens              ^>=2.2+    , interpolatedstring-perl6  ^>=1.0+    , language-javascript       >=0.6.0   && <0.8+    , lens                      ^>=5.0+    , lucid                     ^>=2.9+    , mtl                       ^>=2.2.2+    , scotty                    >=0.11.5  && <0.13+    , text                      ^>=1.2.3+    , transformers              ^>=0.5.6+    , unordered-containers      ^>=0.2.10+    , wai-middleware-static     ^>=0.9+    , wai-websockets            ^>=3.0.1.2+    , websockets                ^>=0.12++  default-language:   Haskell2010   default-extensions:-    NegativeLiterals-    NoImplicitPrelude-    OverloadedStrings-    UnicodeSyntax   ghc-options:-    -Wall-    -Wcompat-    -Wincomplete-record-updates-    -Wincomplete-uni-patterns-    -Wredundant-constraints-    -funbox-strict-fields-    -fforce-recomp-    -threaded-    -rtsopts-    -with-rtsopts=-N+    -Wall -Wcompat -Wincomplete-record-updates+    -Wincomplete-uni-patterns -Wredundant-constraints -fwrite-ide-info+    -hiedir=.hie -test-suite test-  type: exitcode-stdio-1.0-  main-is: test.hs-  hs-source-dirs:-    test+executable web-rep-example+  main-is:            rep-example.hs+  hs-source-dirs:     app   build-depends:-    base >= 4.12 && <5,-    doctest >= 0.16 && < 1.0,-    lens >= 4.17 && < 4.20,-    lucid >= 2.9.11 && < 2.10,-    numhask >= 0.7 && < 0.8,-    tasty >= 1.2.3 && < 1.3,-    tasty-hspec >= 1.1.5 && < 1.2,-    text >= 1.2.3 && < 1.3,-    web-rep-  default-language: Haskell2010+    , base              >=4.12 && <5+    , optparse-generic  >=1.3+    , web-rep++  default-language:   Haskell2010   default-extensions:-    NegativeLiterals-    NoImplicitPrelude-    OverloadedStrings-    UnicodeSyntax   ghc-options:-    -Wall-    -Wcompat-    -Wincomplete-record-updates-    -Wincomplete-uni-patterns-    -Wredundant-constraints-    -funbox-strict-fields-    -fforce-recomp-    -threaded-    -rtsopts-    -with-rtsopts=-N+    -Wall -Wcompat -Wincomplete-record-updates+    -Wincomplete-uni-patterns -Wredundant-constraints+    -funbox-strict-fields -fforce-recomp -threaded -rtsopts+    -with-rtsopts=-N -fwrite-ide-info -hiedir=.hie