noli (empty) → 0.1.0.0
raw patch · 7 files changed
+237/−0 lines, 7 filesdep +basedep +cmarkdep +directorysetup-changed
Dependencies added: base, cmark, directory, lucid, regex-compat, text
Files
- LICENSE +30/−0
- README.md +1/−0
- Setup.hs +2/−0
- noli.cabal +27/−0
- src/Noli.hs +76/−0
- src/Noli/Types.hs +41/−0
- src/PostUtils.hs +60/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Author name here (c) 2020++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Author name here nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,1 @@+# noli
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ noli.cabal view
@@ -0,0 +1,27 @@+name: noli+version: 0.1.0.0+synopsis: A static site generator+description: A static site generator+homepage: https://github.com/githubuser/noli#readme+license: BSD3+license-file: LICENSE+author: Author name here+maintainer: example@example.com+copyright: 2020 Author name here+category: Web+build-type: Simple+cabal-version: >=1.10+extra-source-files: README.md++library+ hs-source-dirs: src+ exposed-modules: Noli,+ Noli.Types+ other-modules: PostUtils+ default-language: Haskell2010+ build-depends: base >= 4.7 && < 5,+ text,+ lucid,+ cmark,+ directory,+ regex-compat
+ src/Noli.hs view
@@ -0,0 +1,76 @@+module Noli+ ( buildProject,+ buildSite,+ compilePosts,+ )+where++import Control.Monad+ ( unless,+ when,+ )+import Data.Text (unpack)+import qualified Data.Text.IO as DT+import Data.Text.Internal (Text)+import Data.Text.Lazy (toStrict)+import Lucid.Base+ ( Html,+ renderText,+ )+import Noli.Types+import PostUtils+ ( compilePost,+ copyFolder,+ extname,+ )+import System.Directory+ ( createDirectory,+ doesDirectoryExist,+ listDirectory,+ removeDirectoryRecursive,+ setCurrentDirectory,+ )++-- | Compile the posts starting from the templates and the folder where the markdown files are.+compilePosts :: PostTemplate -> FilePath -> IO [Post]+compilePosts postCompiler fp = do+ filePaths <- listDirectory fp+ let markdownFilePaths =+ Prelude.map (fp ++) $+ Prelude.filter (\fn -> extname fn == "md") filePaths+ mapM (compilePost postCompiler) markdownFilePaths++-- | Build the dynamic `Project` which works as a basis for the final static site.+-- It takes three arguments and returns a compiled `Project`+buildProject :: Settings -> [Page] -> PostTemplate -> IO Project+buildProject settings pages postCompiler = do+ ps <- compilePosts postCompiler $ posts_location settings+ return Project {posts = ps, pages = pages}++-- | Build the final static site+-- Takes two arguments and creates the files needed for the static site, saving them in the previously-set `dist_location`.+buildSite :: Settings -> Project -> IO ()+buildSite settings' project = do+ distExists <- doesDirectoryExist $ dist_location settings'+ when distExists $ removeDirectoryRecursive $ dist_location settings'+ createDirectory $ dist_location settings'+ copyFolder+ (static_location settings')+ (dist_location settings' ++ "static/")+ setCurrentDirectory $ dist_location settings'+ Prelude.mapM_+ ( \p ->+ DT.writeFile+ (unpack (pagename p) ++ ".html")+ (toStrict $ renderText $ template p)+ )+ (pages project)+ createDirectory "./posts"+ setCurrentDirectory "./posts"+ Prelude.mapM_+ ( \p ->+ DT.writeFile+ (unpack (filename p) ++ ".html")+ (toStrict $ renderText $ compiled_html p)+ )+ (posts project)
+ src/Noli/Types.hs view
@@ -0,0 +1,41 @@+module Noli.Types (Settings (..), Post (..), Project (..), Page (..), PostTemplate) where++import Data.Text+import Lucid.Base++-- | The "dynamic" representation of the static site+data Project = Project+ { posts :: [Post],+ pages :: [Page]+ }++data Settings = Settings+ { -- | The name of the site+ name :: Text,+ -- | The author's full name+ author :: Text,+ -- | The path to the folder that contains the markdown files+ posts_location :: FilePath,+ -- | The path to where the compiled static site will be saved+ dist_location :: FilePath,+ -- | The path to the static folder+ static_location :: FilePath+ }++data Post = Post+ { title :: Text,+ location :: FilePath,+ filename :: Text,+ raw :: Text,+ raw_html :: Text,+ compiled_html :: Html ()+ }+ deriving (Show)++data Page = Page {pagename :: Text, template :: Html ()}++type PostTitle = Text++type PostBody = Text++type PostTemplate = PostTitle -> PostBody -> Html ()
+ src/PostUtils.hs view
@@ -0,0 +1,60 @@+module PostUtils (compilePost, extname, copyFolder) where++import CMark (commonmarkToHtml)+import Control.Monad (unless)+import Data.Maybe (fromJust)+import Data.Text (pack)+import Data.Text.Internal (Text)+import Lucid.Base (Html)+import Noli.Types+import System.Directory (copyFile, createDirectory, doesDirectoryExist, doesFileExist, listDirectory)+import Text.Regex (Regex, matchRegex, mkRegex)++postNameRegex :: Regex+postNameRegex = mkRegex "/?([A-Za-z_]+)\\.md$"++extname :: FilePath -> FilePath+extname = Prelude.reverse . Prelude.takeWhile ('.' /=) . Prelude.reverse++compilePost :: PostTemplate -> FilePath -> IO Post+compilePost postCompiler fp = do+ fileContents <- pack <$> readFile fp+ let compiledFileContents = commonmarkToHtml [] fileContents+ fn = fromJust $ getPostFileName fp+ t = getPostName fn+ compiledHtml = postCompiler (pack t) compiledFileContents+ return+ Post+ { title = pack t,+ location = fp,+ filename = pack fn,+ raw = fileContents,+ raw_html = compiledFileContents,+ compiled_html = compiledHtml+ }++getPostFileName :: FilePath -> Maybe String+getPostFileName fp = case match of+ Nothing -> Nothing+ Just (x : xs) -> Just x+ where+ match = matchRegex postNameRegex fp++getPostName :: String -> String+getPostName fn =+ let repl '_' = ' '+ repl x = x+ in Prelude.map repl fn++copyFolder :: FilePath -> FilePath -> IO ()+copyFolder source destination = do+ destinationExists <- doesDirectoryExist destination+ unless destinationExists $ createDirectory destination+ sourceFiles <- listDirectory source+ Prelude.mapM_ copyFile' sourceFiles+ where+ copyFile' source_file = do+ is_file <- doesFileExist $ source ++ source_file+ if is_file+ then copyFile (source ++ source_file) (destination ++ source_file)+ else copyFolder (source ++ source_file ++ "/") (destination ++ source_file ++ "/")