packages feed

servant-static-th 0.2.4.0 → 1.0.0.0

raw patch · 8 files changed

+77/−18 lines, 8 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Servant.Static.TH.Internal.Server: combineMultiWithServantOr :: NonEmpty (Q Exp) -> Q Exp
- Servant.Static.TH.Internal.Server: combineWithServantOr :: NonEmpty (Q Exp) -> Q Exp
+ Servant.Static.TH.Internal.Server: combineWithServantOr :: Q Exp -> Q Exp -> Q Exp

Files

CHANGELOG.md view
@@ -1,5 +1,13 @@ # Changelog +## v1.0.0.0++-   Serve any files called `index.html` on the root `/` as well as the path+    `/index.html`. This makes servant-static-th operate similar to how web+    servers like Apache or nginx work.+    [#19](https://github.com/cdepillabout/servant-static-th/pull/19).+    Thanks [@vendamere](https://github.com/vendamere)!+ ## v0.2.4.0  -   Add a mime type for .wasm files
servant-static-th.cabal view
@@ -1,5 +1,5 @@ name:                servant-static-th-version:             0.2.4.0+version:             1.0.0.0 synopsis:            Embed a directory of static files in your Servant server description:         Please see <https://github.com/cdepillabout/servant-static-th#readme README.md>. homepage:            https://github.com/cdepillabout/servant-static-th
src/Servant/Static/TH.hs view
@@ -22,14 +22,17 @@   dir\/   ├── js   │   └── test.js-  └── hello.html+  ├── hello.html+  └── index.html @ -Here's the contents of @\"hello.html\"@ and @\"js\/test.js\"@:+Here's the contents of @\"hello.html\"@, @\"index.html\"@, and @\"js\/test.js\"@:  @-  $ cat dir\/index.html+  $ cat dir\/hello.html   \<p\>Hello World\<\/p\>+  $ cat dir\/index.html+  \<p\>This is the index\<\/p\>   $ cat dir\/js\/test.js   console.log(\"hello world\"); @@@ -60,19 +63,30 @@  @   type FrontEndAPI =-         \"js\" 'Servant.API.:>' \"test.js\" 'Servant.API.:>' 'Servant.API.Get' \'['JS'] 'Data.ByteString.ByteString'+    -- index.html is served on the root, as well as from the path \"/index.html\".+         'Servant.API.Get' \'['HTML'] 'Html'     ':<|>' \"index.html\" 'Servant.API.:>' 'Servant.API.Get' \'['HTML'] 'Html'+    -- hello.html is served from the path \"/hello.html\".+    ':<|>' \"hello.html\" 'Servant.API.:>' 'Servant.API.Get' \'['HTML'] 'Html'+    -- js/test.js is served from the path \"/js/test.js\".+    ':<|>' \"js\" 'Servant.API.:>' \"test.js\" 'Servant.API.:>' 'Servant.API.Get' \'['JS'] 'Data.ByteString.ByteString'    frontEndServer :: 'Applicative' m => 'Servant.Server.ServerT' FrontEndAPI m   frontEndServer =-         'pure' "console.log(\\"hello world\\");"+         'pure' "\<p\>This is the index\<\/p\>"+    ':<|>' 'pure' "\<p\>This is the index\<\/p\>"     ':<|>' 'pure' "\<p\>Hello World\<\/p\>"+    ':<|>' 'pure' "console.log(\\"hello world\\");" @  If this WAI application is running, it is possible to use @curl@ to access the server:  @+  $ curl localhost:8080\/+  \<p\>This is the index\<\/p\>+  $ curl localhost:8080\/index.html+  \<p\>This is the index\<\/p\>   $ curl localhost:8080\/hello.html   \<p\>Hello World\<\/p\>   $ curl localhost:8080\/js\/test.js
src/Servant/Static/TH/Internal/Api.hs view
@@ -21,8 +21,13 @@ fileTreeToApiType (FileTreeFile filePath _) = do   addDependentFile filePath   MimeTypeInfo mimeT respT _ <- extensionToMimeTypeInfoEx filePath-  let fileNameLitT = litT $ strTyLit $ takeFileName filePath-  [t|$(fileNameLitT) :> Get '[$(mimeT)] $(respT)|]+  let fileName = takeFileName filePath+  let fileNameLitT = litT $ strTyLit fileName+  case fileName of+    -- We special-case files called "index.html" and generate a type that serves on both+    -- the root, and under the path "index.html".+    "index.html" -> [t|Get '[$(mimeT)] $(respT) :<|> $(fileNameLitT) :> Get '[$(mimeT)] $(respT)|]+    _ -> [t|$(fileNameLitT) :> Get '[$(mimeT)] $(respT)|] fileTreeToApiType (FileTreeDir filePath fileTrees) =   let fileNameLitT = litT $ strTyLit $ takeFileName filePath   in [t|$(fileNameLitT) :> $(combineWithServantOrT nonEmptyApiTypesQ)|]@@ -40,7 +45,7 @@  -- | Take a template directory argument as a 'FilePath' and create a Servant -- type representing the files in the directory.  Empty directories will be--- ignored.+-- ignored. @index.html@ files will also be served at the root. -- -- For example, assume the following directory structure: --@@ -66,6 +71,7 @@ -- @ --   type FrontEndAPI = --          \"js\" ':>' \"test.js\" ':>' 'Get' \'['JS'] 'Data.ByteString.ByteString'+--     ':<|>' 'Get' \'['Servant.HTML.Blaze.HTML'] 'Text.Blaze.Html.Html' --     ':<|>' \"index.html\" ':>' 'Get' \'['Servant.HTML.Blaze.HTML'] 'Text.Blaze.Html.Html' -- @ createApiType
src/Servant/Static/TH/Internal/Server.hs view
@@ -5,34 +5,47 @@ module Servant.Static.TH.Internal.Server where  import Data.Foldable (foldl1)-import Data.List.NonEmpty (NonEmpty)+import Data.List.NonEmpty (NonEmpty((:|))) import Language.Haskell.TH        (Dec, Exp, Q, appE, clause, conT, funD, mkName, normalB,         runIO, sigD) import Language.Haskell.TH.Syntax (addDependentFile) import Servant.API ((:<|>)((:<|>))) import Servant.Server (ServerT)+import System.FilePath (takeFileName) + import Servant.Static.TH.Internal.FileTree import Servant.Static.TH.Internal.Mime  combineWithExp :: Q Exp -> Q Exp -> Q Exp -> Q Exp combineWithExp combiningExp = appE . appE combiningExp -combineWithServantOr :: NonEmpty (Q Exp) -> Q Exp-combineWithServantOr = foldl1 $ combineWithExp [e|(:<|>)|]+combineWithServantOr :: Q Exp -> Q Exp -> Q Exp+combineWithServantOr = combineWithExp [e|(:<|>)|] +combineMultiWithServantOr :: NonEmpty (Q Exp) -> Q Exp+combineMultiWithServantOr = foldl1 combineWithServantOr+ fileTreeToServer :: FileTree -> Q Exp fileTreeToServer (FileTreeFile filePath fileContents) = do   addDependentFile filePath   MimeTypeInfo _ _ contentToExp <- extensionToMimeTypeInfoEx filePath-  contentToExp fileContents+  let fileName = takeFileName filePath+  case fileName of+    "index.html" ->+      combineWithServantOr+        -- content to serve on the root+        (contentToExp fileContents)+        -- content to serve on the path "index.html"+        (contentToExp fileContents)+    _ -> contentToExp fileContents fileTreeToServer (FileTreeDir _ fileTrees) =-  combineWithServantOr $ fmap fileTreeToServer fileTrees+  combineMultiWithServantOr $ fmap fileTreeToServer fileTrees  -- | Take a template directory argument as a 'FilePath' and create a 'ServerT' -- function that serves the files under the directory.  Empty directories will--- be ignored.+-- be ignored. 'index.html' files will also be served at the root. -- -- Note that the file contents will be embedded in the function.  They will -- not be served dynamically at runtime.  This makes it easy to create a@@ -73,6 +86,7 @@ -- @ --   type FrontEndAPI = --          \"js\" 'Servant.API.:>' \"test.js\" 'Servant.API.:>' 'Servant.API.Get' \'['JS'] 'Data.ByteString.ByteString'+--     ':<|>' 'Servant.API.Get' \'['Servant.HTML.Blaze.HTML'] 'Text.Blaze.Html.Html' --     ':<|>' \"index.html\" 'Servant.API.:>' 'Servant.API.Get' \'['Servant.HTML.Blaze.HTML'] 'Text.Blaze.Html.Html' -- --   frontEndServer :: 'Applicative' m => 'ServerT' FrontEndAPI m@@ -85,7 +99,7 @@   -> Q Exp createServerExp templateDir = do   fileTree <- runIO $ getFileTreeIgnoreEmpty templateDir-  combineWithServantOr $ fmap fileTreeToServer fileTree+  combineMultiWithServantOr $ fmap fileTreeToServer fileTree  -- | This is similar to 'createServerExp', but it creates the whole function -- declaration.
test/Spec/ApiSpec.hs view
@@ -19,7 +19,7 @@ $(createApiDec "FrontEndApi" testDir)  type ExpectedFrontEndApi =-    (+    ((       "dir" :>         (           ( "inner-file.html" :> Get '[HTML] Html )@@ -28,7 +28,9 @@         )     )   :<|>-    ( "hello.html" :> Get '[HTML] Html )+    ( "hello.html" :> Get '[HTML] Html ))+  :<|>+    ( Get '[HTML] Html :<|> "index.html" :> Get '[HTML] Html )  checkFrontEndApiType :: ExpectedFrontEndApi :~: FrontEndApi checkFrontEndApiType = Refl
test/Spec/HelperFuncSpec.hs view
@@ -58,6 +58,9 @@               , FileTreeFile                   (testDir </> "hello.html")                   "Hello World\n"+              , FileTreeFile+                  (testDir </> "index.html")+                  "Index\n"               ]         actualFileTree @?= expectedFileTree     , testCase "fails on empty directory" $
test/Spec/ServerSpec.hs view
@@ -33,6 +33,18 @@                 { matchHeaders = ["Content-Type" <:> "text/html;charset=utf-8"]                 }         in get "hello.html" `shouldRespondWith` expectedResp+      it "index.html responds correctly and is html" $+        let expectedResp =+              "Index\n"+                { matchHeaders = ["Content-Type" <:> "text/html;charset=utf-8"]+                }+        in get "index.html" `shouldRespondWith` expectedResp+      it "root responds with index.html contents" $+        let expectedResp =+              "Index\n"+                { matchHeaders = ["Content-Type" <:> "text/html;charset=utf-8"]+                }+        in get "" `shouldRespondWith` expectedResp       it "dir/inner-file.html responds correctly" $         get "dir/inner-file.html" `shouldRespondWith` "Inner File\n"       it "non existing file gives 404" $