microformats2-parser 1.0.1.9 → 1.0.2.0
raw patch · 11 files changed
+166/−106 lines, 11 filesdep +aws-lambda-haskell-runtimedep +aws-lambda-haskell-runtime-waidep +case-insensitivenew-component:exe:microformats2-parser-lambda
Dependencies added: aws-lambda-haskell-runtime, aws-lambda-haskell-runtime-wai, case-insensitive, githash, lens, wai
Files
- README.md +6/−7
- executable/Lambda.hs +9/−0
- executable/Main.hs +0/−74
- executable/Server.hs +7/−0
- executable/WebApp.hs +85/−0
- library/Data/Microformats2/Jf2.hs +1/−0
- library/Data/Microformats2/Parser.hs +9/−9
- library/Data/Microformats2/Parser/HtmlUtil.hs +1/−1
- library/Data/Microformats2/Parser/Property.hs +9/−8
- library/Data/Microformats2/Parser/UnsafeUtil.hs +2/−1
- microformats2-parser.cabal +37/−6
README.md view
@@ -1,5 +1,4 @@ [](https://hackage.haskell.org/package/microformats2-parser)-[](https://travis-ci.org/myfreeweb/microformats2-parser) [](http://unlicense.org) # microformats2-parser@@ -19,11 +18,11 @@ Also check out [http-link-header] because you often need to read links from the Link header! [Microformats 2]: http://microformats.org/wiki/microformats2-[#IndieWeb]: http://indiewebcamp.com-[sweetroll]: https://github.com/myfreeweb/sweetroll+[#IndieWeb]: https://indieweb.org+[sweetroll]: https://github.com/unrelentingtech/sweetroll [tagstream-conduit]: https://hackage.haskell.org/package/tagstream-conduit [JF2]: https://www.w3.org/TR/jf2/-[http-link-header]: https://github.com/myfreeweb/http-link-header+[http-link-header]: https://github.com/unrelentingtech/http-link-header ## [DEMO PAGE](https://unrelenting.technology/mf2/) @@ -71,11 +70,11 @@ Please feel free to submit pull requests! -By participating in this project you agree to follow the [Contributor Code of Conduct](http://contributor-covenant.org/version/1/4/) and to release your contributions under the Unlicense.+By participating in this project you agree to follow the [Contributor Code of Conduct](https://www.contributor-covenant.org/version/1/4/code-of-conduct/) and to release your contributions under the Unlicense. -[The list of contributors is available on GitHub](https://github.com/myfreeweb/microformats2-parser/graphs/contributors).+[The list of contributors is available on GitHub](https://github.com/unrelentingtech/microformats2-parser/graphs/contributors). ## License This is free and unencumbered software released into the public domain. -For more information, please refer to the `UNLICENSE` file or [unlicense.org](http://unlicense.org).+For more information, please refer to the `UNLICENSE` file or [unlicense.org](https://unlicense.org).
+ executable/Lambda.hs view
@@ -0,0 +1,9 @@+{-# LANGUAGE OverloadedStrings, UnicodeSyntax #-}+module Main (main) where++import Aws.Lambda+import Aws.Lambda.Wai+import WebApp++main ∷ IO ()+main = runWaiAsProxiedHttpLambda defaultDispatcherOptions (ignoreALBPathPart "mf2") (HandlerName "mf2") app
− executable/Main.hs
@@ -1,74 +0,0 @@-{-# OPTIONS_GHC -fno-warn-missing-signatures -fno-warn-unused-do-bind #-}-{-# LANGUAGE NoImplicitPrelude, OverloadedStrings, UnicodeSyntax #-}--module Main (main) where--import Prelude.Compat-import Data.Microformats2.Parser-import Data.Aeson.Encode.Pretty-import Data.Aeson.Types (object)-import Data.Default-import qualified Data.Text.Lazy as TL-import Network.Wai.Cli-import Network.Wai.Middleware.Autohead-import Network.URI (parseURI)-import Web.Scotty hiding (html)-import Text.Blaze.Html5 as H hiding (main, param, object, base)-import Text.Blaze.Html5.Attributes as A-import Text.Blaze.Html.Renderer.Utf8 (renderHtml)---exampleValue = "<body> <p class='h-adr'> <span class='p-street-address'>17 Austerstræti</span> <span class='p-locality'>Reykjavík</span> <span class='p-country-name'>Iceland</span> <span class='p-postal-code'>107</span> </p> <div class='h-card'> <a class='p-name u-url' href='http://blog.lizardwrangler.com/' >Mitchell Baker</a> (<a class='p-org h-card' href='http://mozilla.org/' >Mozilla Foundation</a>) </div> <article class='h-entry'> <h1 class='p-name'>Microformats are amazing</h1> <p>Published by <a class='p-author h-card' href='http://example.com'>W. Developer</a> on <time class='dt-published' datetime='2013-06-13 12:00:00'>13<sup>th</sup> June 2013</time> <p class='p-summary'>In which I extoll the virtues of using microformats.</p> <div class='e-content'> <p>Blah blah blah</p> </div> </article> <span class='h-cite'> <time class='dt-published'>YYYY-MM-DD</time> <span class='p-author h-card'>AUTHOR</span>: <cite><a class='u-url p-name' href='URL'>TITLE</a></cite> </span> </body>"--homePage ∷ TL.Text → Html-homePage v = docTypeHtml $ do- H.head $ do- H.meta ! charset "utf-8"- H.title "microformats2-parser"- H.style "body { font-family: 'Helvetica Neue', sans-serif; max-width: 900px; margin: 0 auto; } a { color: #ba2323; } a:hover { color: #da4343; } pre, input, textarea, button { width: 100%; } input, textarea { margin-bottom: 1em; } textarea { resize: vertical; min-height: 15em; } pre { white-space: pre-wrap; } footer { margin: 2em 0; }"- H.body $ do- H.header $ do- h1 $ do- a ! href "https://github.com/myfreeweb/microformats2-parser" $ "microformats2-parser"- a ! href "https://github.com/myfreeweb/microformats2-parser" $ img ! alt "GitHub" ! src "https://img.shields.io/badge/git-hub-gray.svg?style=flat"- " "- a ! href "https://hackage.haskell.org/package/microformats2-parser" $ img ! alt "Hackage" ! src "https://img.shields.io/hackage/v/microformats2-parser.svg?style=flat"- " "- a ! href "https://travis-ci.org/myfreeweb/microformats2-parser" $ img ! alt "Build Status" ! src "https://img.shields.io/travis/myfreeweb/microformats2-parser.svg?style=flat"- " "- a ! href "http://unlicense.org" $ img ! alt "unlicense" ! src "https://img.shields.io/badge/un-license-green.svg?style=flat"- p "This is a test page for the Microformats 2 Haskell parser."- p "Notes:"- ul $ do- li "this demo page uses the Sanitize mode for e-*"- li $ do- a ! href "http://enable-cors.org" $ "CORS is enabled"- " on the endpoint (POST parse.json, form-urlencoded, 'html' and 'base' parameter)"- li $ a ! href "http://jsonview.com" $ "JSONView is awesome"- H.form ! method "post" ! action "parse.json" $ do- textarea ! name "html" $ toHtml v- input ! name "base" ! type_ "url" ! placeholder "https://example.com/base/url/for/resolving/relative/urls"- button "Parse!"- footer $ do- a ! href "https://unrelenting.technology" ! rel "author" $ "unrelenting.technology"--app = scottyApp $ do- middleware autohead-- get "/" $ do- setHeader "Content-Type" "text/html; charset=utf-8"- raw $ renderHtml $ homePage exampleValue-- get "/parse.json" $ do- setHeader "Access-Control-Allow-Origin" "*"- json $ object []-- post "/parse.json" $ do- hsrc ← param "html"- base ← param "base" `rescue` (\_ → return "")- setHeader "Content-Type" "application/json; charset=utf-8"- setHeader "Access-Control-Allow-Origin" "*"- let root = documentRoot $ parseLBS hsrc- raw $ encodePretty $ parseMf2 (def { baseUri = parseURI base }) root--main = defWaiMain =<< app
+ executable/Server.hs view
@@ -0,0 +1,7 @@+{-# OPTIONS_GHC -fno-warn-missing-signatures -fno-warn-unused-do-bind #-}+module Main (main) where++import Network.Wai.Cli+import WebApp++main = defWaiMain =<< app
+ executable/WebApp.hs view
@@ -0,0 +1,85 @@+{-# LANGUAGE NoImplicitPrelude, OverloadedStrings, UnicodeSyntax, TemplateHaskell #-}++module WebApp (app) where++import Prelude.Compat+import Data.Microformats2.Parser+import Data.Microformats2.Jf2+import Data.Aeson.Encode.Pretty+import Data.Aeson.Types (object)+import Data.Default+import qualified Data.Text.Lazy as TL+import Network.Wai (Application)+import Network.Wai.Middleware.Autohead+import Network.URI (parseURI)+import Web.Scotty hiding (html)+import Text.Blaze.Html5 as H hiding (main, param, object, base)+import Text.Blaze.Html5.Attributes as A hiding (id)+import Text.Blaze.Html.Renderer.Utf8 (renderHtml)+import GitHash+++exampleValue ∷ TL.Text+exampleValue = "<body> <p class='h-adr'> <span class='p-street-address'>17 Austerstræti</span> <span class='p-locality'>Reykjavík</span> <span class='p-country-name'>Iceland</span> <span class='p-postal-code'>107</span> </p> <div class='h-card'> <a class='p-name u-url' href='http://blog.lizardwrangler.com/' >Mitchell Baker</a> (<a class='p-org h-card' href='http://mozilla.org/' >Mozilla Foundation</a>) </div> <article class='h-entry'> <h1 class='p-name'>Microformats are amazing</h1> <p>Published by <a class='p-author h-card' href='http://example.com'>W. Developer</a> on <time class='dt-published' datetime='2013-06-13 12:00:00'>13<sup>th</sup> June 2013</time> <p class='p-summary'>In which I extoll the virtues of using microformats.</p> <div class='e-content'> <p>Blah blah blah</p> </div> </article> <span class='h-cite'> <time class='dt-published'>YYYY-MM-DD</time> <span class='p-author h-card'>AUTHOR</span>: <cite><a class='u-url p-name' href='URL'>TITLE</a></cite> </span> </body>"++homePage ∷ TL.Text → Html+homePage v = docTypeHtml $ do+ H.head $ do+ H.meta ! charset "utf-8"+ H.title "microformats2-parser"+ H.style "body { font-family: 'Helvetica Neue', sans-serif; max-width: 900px; margin: 0 auto; } a { color: #ba2323; } a:hover { color: #da4343; } pre, input:not([type=checkbox]), textarea, button { width: 100%; } input:not([type=checkbox]), textarea, label { margin-bottom: 1em; display: block; } textarea { resize: vertical; min-height: 15em; } pre { white-space: pre-wrap; } footer { margin: 2em 0; }"+ H.body $ do+ H.header $ do+ h1 $ do+ a ! href "https://github.com/unrelentingtech/microformats2-parser" $ "microformats2-parser"+ a ! href "https://github.com/unrelentingtech/microformats2-parser" $ img ! alt "GitHub" ! src "https://img.shields.io/badge/git-hub-gray.svg?style=flat"+ " "+ a ! href "https://hackage.haskell.org/package/microformats2-parser" $ img ! alt "Hackage" ! src "https://img.shields.io/hackage/v/microformats2-parser.svg?style=flat"+ " "+ a ! href "https://unlicense.org" $ img ! alt "unlicense" ! src "https://img.shields.io/badge/un-license-green.svg?style=flat"+ p "This is a test page for the Microformats 2 Haskell parser."+ p "Notes:"+ ul $ do+ li "this demo page uses the Sanitize mode for e-*"+ li $ do+ a ! href "https://enable-cors.org" $ "CORS is enabled"+ " on the endpoint (POST parse.json, form-urlencoded, 'html' and 'base' parameter)"+ H.form ! method "post" ! action "parse.json" $ do+ textarea ! name "html" $ toHtml v+ input ! name "base" ! type_ "url" ! placeholder "https://example.com/base/url/for/resolving/relative/urls"+ H.label $ do+ input ! name "jf2" ! type_ "checkbox"+ "Return "+ a ! href "https://indieweb.org/jf2" $ "jf2"+ " instead of full MF2 JSON"+ button "Parse!"+ footer $ do+ let gi = $$tGitInfoCwd+ p $ do+ "Version: "+ a ! href (toValue $ "https://github.com/unrelentingtech/microformats2-parser/commit/" <> giHash gi) $ toMarkup $ take 12 $ giHash gi+ " ("+ toMarkup $ giCommitDate gi+ ")"+ a ! href "https://unrelenting.technology" ! rel "author" $ "unrelenting.technology"++app ∷ IO Application+app = scottyApp $ do+ middleware autohead++ get "/" $ do+ setHeader "Content-Type" "text/html; charset=utf-8"+ raw $ renderHtml $ homePage exampleValue++ get "/parse.json" $ do+ setHeader "Access-Control-Allow-Origin" "*"+ json $ object []++ post "/parse.json" $ do+ hsrc ← param "html"+ base ← param "base" `rescue` (\_ → return "")+ jf2 ← param "jf2" `rescue` (\_ → return ("" ∷ TL.Text))+ setHeader "Content-Type" "application/json; charset=utf-8"+ setHeader "Access-Control-Allow-Origin" "*"+ let root = documentRoot $ parseLBS hsrc+ raw $ encodePretty $ (if jf2 /= "" then mf2ToJf2 else id) $ parseMf2 (def { baseUri = parseURI base }) root
library/Data/Microformats2/Jf2.hs view
@@ -3,6 +3,7 @@ module Data.Microformats2.Jf2 (mf2ToJf2) where import Prelude.Compat+import Control.Lens import Data.Maybe import Data.Aeson.Lens import qualified Data.HashMap.Strict as HMS
library/Data/Microformats2/Parser.hs view
@@ -64,7 +64,7 @@ addImpliedProperties settings e v@(Object o) = Object $ addIfNull "photo" "photo" resolveURI' $ addIfNull "url" "url" resolveURI' $ addIfNullAndNoOthers "name" "name" id o where addIfNull nameJ nameH f obj = if isNothing $ v ^? key nameJ then HMS.insert nameJ (vsingleton $ f <$> implyProperty nameH e) obj else obj addIfNullAndNoOthers nameJ nameH f obj =- if isNothing (v ^? key nameJ) && isNothing (v ^? key "children") && isNothing (e ^? plate . entire . peElements)+ if isNothing (v ^? key nameJ) && isNothing (v ^? key "children") && isNothing (e ^? plate . cosmos . peElements) then HMS.insert nameJ (vsingleton $ f <$> implyProperty nameH e) obj else obj peElements = attributeSatisfies "class" $ any (\x → isPClass x || isEClass x) . T.split isSpace resolveURI' = resolveURI $ baseUri settings@@ -72,7 +72,7 @@ removePropertiesOfNestedMicroformats ∷ [Element] → [Element] → [Element] removePropertiesOfNestedMicroformats nmf2s = filter (not . isNested)- where isNested e = any (\e' → e `elem` filter (/= e') (e' ^.. entire)) nmf2s+ where isNested e = any (\e' → e `elem` filter (/= e') (e' ^.. cosmos)) nmf2s parseProperty ∷ Mf2ParserSettings → Element → [Pair] parseProperty settings e =@@ -88,19 +88,19 @@ "type" .= filter isMf2Class (classes e) , "properties" .= properties , "children" .= childrenMf2- , "shape" .= fromMaybe Null (String <$> e ^? el "area" . attr "shape")- , "coords" .= fromMaybe Null (String <$> e ^? el "area" . attr "coords") ]+ , "shape" .= fromMaybe Null (String <$> e ^? named "area" . attr "shape")+ , "coords" .= fromMaybe Null (String <$> e ^? named "area" . attr "coords") ] where childrenMf2 = map ((\x → addValue "p" x Null) . parseH settings) $ filter (not . isProperty) $ deduplicateElements allMf2Descendants- allMf2Descendants = filter (/= e) $ e ^.. entire . mf2Elements+ allMf2Descendants = filter (/= e) $ e ^.. cosmos . mf2Elements -- we have to do all of this because multiple elements can become multiple properties (with overlap) properties = Object $ HMS.filter (not . emptyVal) properties' (Object properties') = addImpliedProperties settings e $ object $ map mergeProps $ groupBy' fst properties''- properties'' = concatMap (parseProperty settings) $ removePropertiesOfNestedMicroformats allMf2Descendants $ filter (/= e) $ e ^.. entire . propertyElements+ properties'' = concatMap (parseProperty settings) $ removePropertiesOfNestedMicroformats allMf2Descendants $ filter (/= e) $ e ^.. cosmos . propertyElements -- | Parses Microformats 2 from an HTML Element into a JSON Value. parseMf2 ∷ Mf2ParserSettings → Element → Value parseMf2 settings rootEl = object [ "items" .= items, "rels" .= rels, "rel-urls" .= relUrls ]- where items = map (parseH settings') $ deduplicateElements $ rootEl' ^.. entire . mf2Elements+ where items = map (parseH settings') $ deduplicateElements $ rootEl' ^.. cosmos . mf2Elements rels = object $ map (\(r, es) → r .= map snd es) $ groupBy' fst $ expandSnd $ map (\e → (T.split isSpace (e ^. attr "rel"), resolveURI (baseUri settings') $ e ^. attr "href")) linkEls relUrls = object $ map relUrlObject linkEls relUrlObject e = resolveURI (baseUri settings') (e ^. attr "href") .= object (filter (not . emptyVal . snd) [@@ -110,9 +110,9 @@ , linkAttr "media" "media" e , linkAttr "hreflang" "hreflang" e ]) linkAttr nameJ nameH e = nameJ .= fromMaybe Null (String <$> e ^? attr nameH)- linkEls = filter (isJust . (^? attr "href")) $ filter (isJust . (^? attr "rel")) $ rootEl' ^.. entire . els [ "a", "link" ]+ linkEls = filter (isJust . (^? attr "href")) $ filter (isJust . (^? attr "rel")) $ rootEl' ^.. cosmos . els [ "a", "link" ] -- Obligatory WTF comment about base[href] being relative to the URI the page was requested from! <https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base>- settings' = settings { baseUri = case (baseUri settings, parseURIReference =<< T.unpack <$> (rootEl' ^? entire . el "base" . attr "href")) of+ settings' = settings { baseUri = case (baseUri settings, parseURIReference =<< T.unpack <$> (rootEl' ^? cosmos . named "base" . attr "href")) of (Just sU, Just tU) → Just (tU `relativeTo` sU) (Just sU, Nothing) → Just sU (Nothing, Just tU) → Just tU
library/Data/Microformats2/Parser/HtmlUtil.hs view
@@ -85,7 +85,7 @@ deduplicateElements ∷ [Element] → [Element] deduplicateElements es = filter (not . isNested) es- where isNested e = any (\e' → e `elem` filter (/= e') (e' ^.. entire)) es+ where isNested e = any (\e' → e `elem` filter (/= e') (e' ^.. cosmos)) es -- not the fastest function I guess... escapeHtml ∷ Text → Text
library/Data/Microformats2/Parser/Property.hs view
@@ -6,6 +6,7 @@ import Prelude.Compat import qualified Data.Text as T import Data.Text (Text)+import Data.CaseInsensitive (CI) import Data.Char (isSpace) import Data.Foldable (asum) import qualified Data.Map as M@@ -43,21 +44,21 @@ getOnlyChildren ∷ Element → [Element] getOnlyChildren e = if lengthOf plate e == 1 then e ^.. plate else [] -getOnlyChild, getOnlyOfType ∷ Name → Element → Maybe Element+getOnlyChild, getOnlyOfType ∷ CI Text → Element → Maybe Element getOnlyChild n e = if' (fromMaybe False $ not <$> isProperty <$> r) $ r- where r = if' (lengthOf plate e == 1) $ e ^? plate . el n+ where r = if' (lengthOf plate e == 1) $ e ^? plate . named n getOnlyOfType n e = if' (fromMaybe False $ not <$> isProperty <$> r) $ r- where r = if' (lengthOf (plate . el n) e == 1) $ e ^? plate . el n+ where r = if' (lengthOf (plate . named n) e == 1) $ e ^? plate . named n els ∷ [Name] → Traversal' Element Element els ns f s = if elementName s `elem` ns then f s else pure s getAbbrTitle, getDataInputValue, getImgSrc, getObjectData, getImgAreaAlt, getAAreaHref, getImgAudioVideoSourceSrc, getTimeInsDelDatetime, getOnlyChildImgAreaAlt, \ getOnlyChildAbbrTitle, getOnlyOfTypeImgSrc, getOnlyOfTypeObjectData, getOnlyOfTypeAAreaHref, extractValue, extractValueTitle ∷ Element → Maybe Text-getAbbrTitle e = e ^. el "abbr" . attribute "title"+getAbbrTitle e = e ^. named "abbr" . attribute "title" getDataInputValue e = e ^. els ["data", "input"] . attribute "value"-getImgSrc e = e ^. el "img" . attribute "src"-getObjectData e = e ^. el "object" . attribute "data"+getImgSrc e = e ^. named "img" . attribute "src"+getObjectData e = e ^. named "object" . attribute "data" getImgAreaAlt e = e ^. els ["img", "area"] . attribute "alt" getAAreaHref e = e ^. els ["a", "area"] . attribute "href" getImgAudioVideoSourceSrc e = e ^. els ["img", "audio", "video", "source"] . attribute "src"@@ -74,8 +75,8 @@ extractValueClassPattern fs e = if' (isJust $ e ^? valueParts) extractValueParts where extractValueParts = Just . catMaybes $ e ^.. valueParts . to extractValuePart extractValuePart e' = asum $ fs <*> pure e'- valueParts ∷ Applicative φ => (Element → φ Element) → Element → φ Element- valueParts = entire . hasOneClass ["value", "value-title"]+ valueParts ∷ (Applicative φ, Contravariant φ) => (Element → φ Element) → Element → φ Element+ valueParts = cosmos . hasOneClass ["value", "value-title"] extractValueClassPatternConcat ∷ [Element → Maybe Text] → Element → Maybe Text extractValueClassPatternConcat fs e = T.concat <$> extractValueClassPattern fs e
library/Data/Microformats2/Parser/UnsafeUtil.hs view
@@ -6,6 +6,7 @@ ) where import Prelude.Compat+import Control.Lens as X hiding (re, (.=)) import Data.Aeson import Data.Aeson.Types as X import Data.Default as X@@ -15,7 +16,7 @@ import qualified Data.Vector as V import qualified Data.HashMap.Strict as HMS import Text.Regex.PCRE.Heavy-import Text.XML.Lens as X hiding (re, (.=))+import Text.XML.Lens as X import Text.HTML.DOM as X (sinkDoc, parseLBS) import Text.Blaze import Text.Blaze.Renderer.Text
microformats2-parser.cabal view
@@ -1,11 +1,11 @@ name: microformats2-parser-version: 1.0.1.9+version: 1.0.2.0 synopsis: A Microformats 2 parser. description: A parser for Microformats 2 (http://microformats.org/wiki/microformats2), a simple way to describe structured information in HTML. category: Web-homepage: https://github.com/myfreeweb/microformats2-parser+homepage: https://github.com/unrelentingtech/microformats2-parser author: Greg V-copyright: 2015-2018 Greg V <greg@unrelenting.technology>+copyright: 2015-2021 Greg V <greg@unrelenting.technology> maintainer: greg@unrelenting.technology license: PublicDomain license-file: UNLICENSE@@ -14,11 +14,11 @@ extra-source-files: README.md tested-with:- GHC == 8.4.3+ GHC == 8.10.4 source-repository head type: git- location: git://github.com/myfreeweb/microformats2-parser.git+ location: git://github.com/unrelentingtech/microformats2-parser.git library build-depends:@@ -27,6 +27,7 @@ , transformers , text , bytestring+ , case-insensitive , time , either , safe@@ -36,6 +37,7 @@ , vector , aeson , aeson-qq+ , lens , lens-aeson , data-default , html-conduit@@ -64,6 +66,7 @@ base >= 4.7.0.0 && < 5 , base-compat >= 0.8.0 , options+ , wai , wai-cli , wai-extra , network@@ -75,11 +78,39 @@ , aeson-pretty , blaze-html , blaze-markup+ , githash , microformats2-parser default-language: Haskell2010 ghc-options: -Wall -threaded -rtsopts "-with-rtsopts=-N -qa -qm" hs-source-dirs: executable- main-is: Main.hs+ main-is: Server.hs+ other-modules: WebApp++executable microformats2-parser-lambda+ build-depends:+ base >= 4.7.0.0 && < 5+ , base-compat >= 0.8.0+ , options+ , aws-lambda-haskell-runtime+ , aws-lambda-haskell-runtime-wai+ , wai+ , wai-extra+ , network+ , network-uri+ , data-default+ , text+ , scotty+ , aeson+ , aeson-pretty+ , blaze-html+ , blaze-markup+ , githash+ , microformats2-parser+ default-language: Haskell2010+ ghc-options: -Wall -threaded -rtsopts "-with-rtsopts=-N -qa -qm"+ hs-source-dirs: executable+ main-is: Lambda.hs+ other-modules: WebApp test-suite tests build-depends: