packages feed

stagen (empty) → 0.0.0

raw patch · 16 files changed

+468/−0 lines, 16 filesdep +aesondep +basedep +blaze-htmlsetup-changed

Dependencies added: aeson, base, blaze-html, bytestring, data-default, directory, filemanip, lucid, markdown, mtl, optparse-applicative, parallel, parsec, stagen, text

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2015, Joe Vargas++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 Joe Vargas 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 @@+# stagen
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ executable/Main.hs view
@@ -0,0 +1,6 @@+module Main where++import qualified Stagen.Main++main :: IO ()+main = Stagen.Main.main
+ library/Stagen/Build.hs view
@@ -0,0 +1,115 @@+module Stagen.Build where++import qualified Data.Text as T+import qualified Data.Text.Lazy as TL+import qualified Data.Text.Lazy.IO as TL+import qualified Data.List as L+import qualified Text.Parsec as P+import qualified Text.Parsec.String as P+import Control.Arrow+import Control.Monad (void)+import System.FilePath.Find+import Data.Monoid+import Data.Maybe+import Lucid++import Stagen.Opts+import Stagen.Date+import Stagen.Template+import Stagen.Page+import Stagen.Job+import Stagen.File++runBuild :: Opts -> IO ()+runBuild opts@Opts{..} = do+    tpl <- mkTemplate opts+    let ignore = optsIgnore ++ catMaybes [optsHeader, optsFooter, optsArchive]+    files <- find (pure True) (eligable ignore) optsTargetDirectory+    htmlPathAndPages <- sequence $ map (fromMarkdown optsVerbose) files+    let archive = archiveJob optsVerbose tpl htmlPathAndPages optsArchive+    let pageAndHtmlPaths = map (\(page, path) -> (path, page)) htmlPathAndPages+    let jobs = archive : map (uncurry (writePage tpl)) pageAndHtmlPaths+    void $ runJobs optsJobs jobs++build :: Template -> Page -> TL.Text+build Template{..} Page{..} = renderText $ doctypehtml_ $ do+    head_ $ do+        void $ title_ (toHtmlRaw pageTitle)+        mapM_ (\href -> link_ [rel_ "stylesheet", type_ "text/css", href_ href]) (map TL.toStrict tplStyleSheets)+        mapM_ (\src -> termWith "script" [src_ src] "") (map TL.toStrict tplScripts)+    body_ . div_ [id_ "wrapper"] $ do+        div_ [id_ "header"] (try tplHeader)+        div_ [id_ "content"] (toHtmlRaw pageContent)+        div_ [id_ "footer"] (try tplFooter)+ where+    try = toHtmlRaw . fromMaybe TL.empty++archiveJob :: Verbose -> Template -> [(FilePath, Page)] -> Maybe FilePath -> IO ()+archiveJob verbose tpl htmlPathAndPages mMdPath = fromMaybe (return ()) $ do+    mdPath <- mMdPath+    return $ do+        (htmlPath, page) <- fromMarkdown verbose mdPath+        writePage tpl (addArchiveEntries page htmlPathAndPages) htmlPath++writePageFromMarkdown :: Verbose -> Template -> FilePath -> IO ()+writePageFromMarkdown verbose tpl mdPath = do+    (htmlPath, page) <- fromMarkdown verbose mdPath+    writePage tpl page htmlPath++writePage :: Template -> Page -> FilePath -> IO ()+writePage tpl page htmlPath = TL.writeFile htmlPath (build tpl page)++addArchiveEntries :: Page -> [(FilePath, Page)] -> Page+addArchiveEntries page htmlPathAndPages =+    let pathAndTitles = map (second pageTitle) htmlPathAndPages+        sorter = L.sortBy (\(_,_,a) (_,_,b)-> compare a b)+        content = (pageContent page) <> (toLinks . sorter . getEntries) pathAndTitles+    in Page{pageTitle = pageTitle page, pageContent = content}+ where+    getEntries :: [(FilePath, TL.Text)] -> [(FilePath, TL.Text, Date)]+    getEntries = catMaybes . map (\(f,t) -> fmap (f,t,) (parseMay datePrefix (baseName f)))++    toLinks :: [(FilePath, TL.Text, Date)] -> TL.Text+    toLinks = renderText . ul_ . toHtmlRaw . TL.concat . map toLink++    toLink :: (FilePath, TL.Text, Date) -> TL.Text+    toLink (path, title, date) =+        renderText+            (li_ (a_ [href_ (T.pack path)]+            (toHtmlRaw $ displayDate date <> " <span id=\"title\">" <> title <> "</span>")))++displayDate :: Date -> TL.Text+displayDate Date{..} = TL.concat+    [ TL.pack (show dateYear)+    , "-"+    , TL.pack (showTwoDigits dateMonth)+    , "-"+    , TL.pack (showTwoDigits dateDay)+    ]+ where+    showTwoDigits n+        | n < 10 = '0' : show n+        | otherwise = show n++parseMay :: P.Parser a -> String -> Maybe a+parseMay p src = case P.parse p "" src of+    Left _ -> Nothing+    Right x -> Just x++baseName :: FilePath -> FilePath+baseName path = basename+ where+    revPath = reverse path+    revName = takeWhile (/= '/') revPath+    basename = reverse (dropWhile (/= '.') revName)++mkTemplate :: Opts -> IO Template+mkTemplate Opts{..} = do+    let tplStyleSheets = map TL.pack optsStyleSheets+    let tplScripts = map TL.pack optsScripts+    tplHeader <- go optsHeader+    tplFooter <- go optsFooter+    return Template{..}+ where+    go Nothing = return Nothing+    go (Just path) = Just <$> render <$> TL.readFile path
+ library/Stagen/Clean.hs view
@@ -0,0 +1,19 @@+module Stagen.Clean where++import Control.Applicative (pure, (<$>))+import Data.Maybe (catMaybes)+import System.FilePath.Find (find)+import System.IO.Error (catchIOError)+import System.Directory (removeFile)++import Stagen.Opts+import Stagen.File++runClean :: Opts -> IO ()+runClean Opts{..} = do+    let ignore = optsIgnore ++ catMaybes [optsHeader, optsFooter]+    files <- find (pure True) (eligable ignore) optsTargetDirectory+    htmlPaths <- map fst <$> (sequence $ map (fromMarkdown optsVerbose) files)+    mapM_ (\p -> catchIOError (removeFile p) (const done)) htmlPaths+ where+    done = return ()
+ library/Stagen/Date.hs view
@@ -0,0 +1,23 @@+module Stagen.Date where++import Data.Functor (void)+import Text.Parsec+import Text.Parsec.String++data Date = Date {+    dateYear :: Int,+    dateMonth :: Int,+    dateDay :: Int+} deriving (Show, Eq, Ord)++datePrefix :: Parser Date+datePrefix = do+    year <- number+    void $ char '-'+    month <- number+    void $ char '-'+    day <- number+    void $ char '-'+    return (Date year month day)+ where+    number = fmap read (many1 digit)
+ library/Stagen/File.hs view
@@ -0,0 +1,47 @@+module Stagen.File where++import qualified Data.Text.Lazy as TL+import qualified Data.Text.Lazy.IO as TL+import Control.Monad (when)+import System.FilePath.Find+import Text.Markdown+import Text.Blaze.Html.Renderer.Text (renderHtml)++import Stagen.Opts+import Stagen.Page++fromMarkdown :: Verbose -> FilePath -> IO (FilePath, Page)+fromMarkdown verbose mdPath = do+    let htmlPath = changeExtension mdPath "html"+    page <- readPage mdPath+    when (verbose == Verbose) (putStrLn htmlPath)+    return (htmlPath, page)++eligable :: [FilePath] -> FindClause Bool+eligable ignore = do+    isMarkdown <- (== ".md") <$> extension+    name <- fileName+    let isFileName = and (map (/= name) ignore)+    return (isMarkdown && isFileName)++readPage :: FilePath -> IO Page+readPage path = do+    content <- TL.readFile path+    let pageTitle = TL.filter (not . isMarkdownChar) (TL.takeWhile isNotNewLine content)+    let pageContent = render content+    return Page{..}+ where+    isMarkdownChar ch = ch == '_' || ch == '*' || ch == '#' || ch == '>'+    isNotNewLine ch = ch /= '\n' && ch /= '\r'++changeExtension :: FilePath -> String -> FilePath+changeExtension path newExtension+    | hasExt = basename ++ newExtension+    | otherwise = path ++ ('.' : newExtension)+ where+    revPath = reverse path+    hasExt = elem '.' (takeWhile (/= '/') revPath)+    basename = reverse (dropWhile (/= '.') revPath)++render :: TL.Text -> TL.Text+render = renderHtml . markdown def
+ library/Stagen/Init.hs view
@@ -0,0 +1,7 @@+module Stagen.Init where++import Stagen.Opts++runInit :: Opts -> IO ()+runInit Opts{..} = do+    return ()
+ library/Stagen/Job.hs view
@@ -0,0 +1,7 @@+module Stagen.Job where++import Control.Monad (sequence)+import Control.Parallel.Strategies++runJobs :: Monad m => Int -> [m a] -> m [a]+runJobs n = sequence . runEval . evalBuffer n rseq
+ library/Stagen/Main.hs view
@@ -0,0 +1,14 @@+module Stagen.Main where++import Stagen.Opts+import Stagen.Init+import Stagen.Build+import Stagen.Clean++main :: IO ()+main = do+    opts <- parseOpts+    runByCommand (optsCommand opts) opts++runByCommand :: Command -> Opts -> IO ()+runByCommand = \case Init -> runInit; Build -> runBuild; Clean -> runClean
+ library/Stagen/Opts.hs view
@@ -0,0 +1,65 @@+module Stagen.Opts where++import qualified Data.Text.Lazy as TL+import Data.Default+import Data.Bool (bool)+import Data.Monoid (mconcat, (<>))+import Options.Applicative+import Options.Applicative.Builder.Internal (HasName)++type TargetDirectory = FilePath++data Verbose = Verbose | Slient+    deriving (Show, Eq)++data Command = Init | Build | Clean+    deriving (Show, Eq)++data Opts = Opts {+    optsCommand :: Command,+    optsHeader :: Maybe FilePath,+    optsFooter :: Maybe FilePath,+    optsArchive :: Maybe FilePath,+    optsStyleSheets :: [FilePath],+    optsScripts :: [FilePath],+    optsIgnore :: [FilePath],+    optsJobs :: Int,+    optsVerbose :: Verbose,+    optsTargetDirectory :: TargetDirectory+} deriving Show++parseOpts :: IO Opts+parseOpts = execParser (info optsP idm)++optsP :: Parser Opts+optsP =+    let choice a c = command a (info (cmdOptsP c) (progDesc a))+        choices = mconcat [choice "init" Init, choice "build" Build, choice "clean" Clean]+    in subparser choices++cmdOptsP :: Command -> Parser Opts+cmdOptsP cmd = Opts cmd+    <$> tryStrArg 'e' "header" "Include header"+    <*> tryStrArg 'f' "footer" "Include footer"+    <*> tryStrArg 'a' "archive" "Prepend archive to generated page"+    <*> many (strArg 'c' "stylesheet" "Stylesheet file path")+    <*> many (strArg 's' "script" "Script file path")+    <*> many (strArg 'i' "ignore" "Don't render this file")+    <*> (option auto (arg 'j' "jobs" "Run ARG jobs simultaneously") <|> pure (optsJobs def))+    <*> fmap (bool Slient Verbose) (switch (arg 'v' "verbose" "Explain what is being done"))+    <*> targetDirectory++arg :: HasName f => Char -> String -> String -> Mod f a+arg s l h = short s <> long l <> help h++strArg :: Char -> String -> String -> Parser String+strArg c l i = strOption (arg c l i)++tryStrArg :: Char -> String -> String -> Parser (Maybe String)+tryStrArg c l i = fmap Just (strArg c l i) <|> pure Nothing++targetDirectory :: Parser TargetDirectory+targetDirectory = strArgument (metavar "TARGET_DIRECTORY") <|> pure (optsTargetDirectory def)++instance Default Opts where+    def = Opts Build Nothing Nothing Nothing [] [] [] 1 Slient "."
+ library/Stagen/Page.hs view
@@ -0,0 +1,12 @@+module Stagen.Page where++import qualified Data.Text.Lazy as TL+import Data.Default++data Page = Page {+    pageTitle :: TL.Text,+    pageContent :: TL.Text+} deriving Show++instance Default Page where+    def = Page TL.empty TL.empty
+ library/Stagen/Template.hs view
@@ -0,0 +1,14 @@+module Stagen.Template where++import qualified Data.Text.Lazy as TL+import Data.Default++data Template = Template {+    tplStyleSheets :: [TL.Text],+    tplScripts :: [TL.Text],+    tplHeader :: Maybe TL.Text,+    tplFooter :: Maybe TL.Text+} deriving Show++instance Default Template where+    def = Template [] [] Nothing Nothing
+ package.yaml view
@@ -0,0 +1,44 @@+name: stagen+version: '0.0.0'+category: Web+synopsis: Static site generator+description: Low dependency static site generator using markdown+maintainer: Joe Vargas+license: BSD3+extra-source-files:+- package.yaml+- README.md+ghc-options: -Wall+default-extensions:+- LambdaCase+- OverloadedStrings+- TupleSections+- RecordWildCards+library:+  dependencies:+  - aeson+  - base >= 4.7 && < 5+  - bytestring+  - blaze-html+  - data-default+  - directory+  - filemanip+  - lucid+  - markdown+  - mtl+  - optparse-applicative+  - parallel+  - parsec+  - text+  source-dirs: library+executables:+  stagen:+    dependencies:+    - base+    - stagen+    ghc-options:+    - -rtsopts+    - -threaded+    - -with-rtsopts=-N+    main: Main.hs+    source-dirs: executable
+ stagen.cabal view
@@ -0,0 +1,62 @@+-- This file has been generated from package.yaml by hpack version 0.17.0.+--+-- see: https://github.com/sol/hpack++name:           stagen+version:        0.0.0+synopsis:       Static site generator+description:    Low dependency static site generator using markdown+category:       Web+maintainer:     Joe Vargas+license:        BSD3+license-file:   LICENSE+build-type:     Simple+cabal-version:  >= 1.10++extra-source-files:+    package.yaml+    README.md++library+  hs-source-dirs:+      library+  default-extensions: LambdaCase OverloadedStrings TupleSections RecordWildCards+  ghc-options: -Wall+  build-depends:+      aeson+    , base >= 4.7 && < 5+    , bytestring+    , blaze-html+    , data-default+    , directory+    , filemanip+    , lucid+    , markdown+    , mtl+    , optparse-applicative+    , parallel+    , parsec+    , text+  exposed-modules:+      Stagen.Build+      Stagen.Clean+      Stagen.Date+      Stagen.File+      Stagen.Init+      Stagen.Job+      Stagen.Main+      Stagen.Opts+      Stagen.Page+      Stagen.Template+  default-language: Haskell2010++executable stagen+  main-is: Main.hs+  hs-source-dirs:+      executable+  default-extensions: LambdaCase OverloadedStrings TupleSections RecordWildCards+  ghc-options: -Wall -rtsopts -threaded -with-rtsopts=-N+  build-depends:+      base+    , stagen+  default-language: Haskell2010