From 5d8872a5aa2573605c6ee2c1070c140e37c8083b Mon Sep 17 00:00:00 2001 From: Mirek Kratochvil Date: Wed, 21 Feb 2024 14:27:36 +0100 Subject: [PATCH 1/7] fix index URL appending with query strings and hashes, expose the index filename to the template --- reploy.hs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/reploy.hs b/reploy.hs index aab4282..c540d5b 100644 --- a/reploy.hs +++ b/reploy.hs @@ -181,7 +181,8 @@ processLink base l = || take 1 l == "#" then pure l else if isAbsolute l - then rootedPageLink l + then let (path, rest) = break (`elem` ['?', '#']) l + in (<> rest) <$> rootedPageLink path else installFile (base l) >>= rootedLink -- | Conjure a function that finds a displayable name for a page at a particular mount. @@ -208,6 +209,8 @@ addGlobalMeta meta = do rt <- rootedLink' rtp <- rootedPageLink' pn <- pageName' + aui <- use appendUrlIndex + ifname <- use indexFilename Y.Object m <- (`objMerge` meta) <$> use extraMeta let l = map (\(k, v) -> (K.toText k, Mu.toMustache v)) $ KM.toList m pure . Mu.object @@ -217,6 +220,7 @@ addGlobalMeta meta = do , ("pageLink", Mu.overText $ T.pack . rtp . T.unpack) , ("pageName", Mu.overText $ T.pack . pn . T.unpack) ] + ++ (if aui then [("indexFilename", Mu.toMustache $ T.pack ifname )] else []) -- | Get the expected timestamp file for a given filepath metadataFile :: FilePath -> Site FilePath From 541217b03386c50725b8dc01c3de8cac718ab612 Mon Sep 17 00:00:00 2001 From: Mirek Kratochvil Date: Mon, 14 Oct 2024 14:10:16 +0200 Subject: [PATCH 2/7] some fixes --- pages/index.md | 2 ++ reploy.hs | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pages/index.md b/pages/index.md index 5fedf2c..b543ec2 100644 --- a/pages/index.md +++ b/pages/index.md @@ -10,3 +10,5 @@ timestamp: null # Well hello there This site is empty, you can start populating it! + +[link test](/?link-test) diff --git a/reploy.hs b/reploy.hs index c540d5b..b94046d 100644 --- a/reploy.hs +++ b/reploy.hs @@ -210,7 +210,7 @@ addGlobalMeta meta = do rtp <- rootedPageLink' pn <- pageName' aui <- use appendUrlIndex - ifname <- use indexFilename + ifi <- use indexFile Y.Object m <- (`objMerge` meta) <$> use extraMeta let l = map (\(k, v) -> (K.toText k, Mu.toMustache v)) $ KM.toList m pure . Mu.object @@ -220,7 +220,9 @@ addGlobalMeta meta = do , ("pageLink", Mu.overText $ T.pack . rtp . T.unpack) , ("pageName", Mu.overText $ T.pack . pn . T.unpack) ] - ++ (if aui then [("indexFilename", Mu.toMustache $ T.pack ifname )] else []) + ++ (if aui + then [("indexFile", Mu.toMustache $ T.pack ifi)] + else []) -- | Get the expected timestamp file for a given filepath metadataFile :: FilePath -> Site FilePath From fc12ce350816959b7722de4ea68e86c2625776d2 Mon Sep 17 00:00:00 2001 From: Mirek Kratochvil Date: Tue, 15 Oct 2024 12:23:57 +0200 Subject: [PATCH 3/7] yarn went crazy, let's do npm only --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index dbe099a..4a35c65 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,7 +17,7 @@ FROM debian:trixie RUN apt -y update && apt -y install \ - yarnpkg git git-lfs ssh rsync ghc cabal-install build-essential pkg-config zlib1g-dev jq yq \ + npm nodejs git git-lfs ssh rsync ghc cabal-install build-essential pkg-config zlib1g-dev jq yq \ && rm -fr /var/cache/apt WORKDIR /opt/reploy From ebad2dcaa5712575a8e6b117910eea0b64f4d2bd Mon Sep 17 00:00:00 2001 From: Mirek Kratochvil Date: Mon, 27 Jan 2025 14:42:53 +0100 Subject: [PATCH 4/7] add mount: URI scheme that allows folks to have somewhat relative links --- reploy.hs | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/reploy.hs b/reploy.hs index b94046d..dbdb091 100644 --- a/reploy.hs +++ b/reploy.hs @@ -46,6 +46,7 @@ import System.FilePath ( isAbsolute , joinPath , splitFileName + , splitPath , takeDirectory , takeFileName ) @@ -175,15 +176,25 @@ rootedLink :: FilePath -> Site FilePath rootedLink = (<*>) rootedLink' . pure -- | Process a single link pointing out from a page. -processLink :: FilePath -> FilePath -> Site String -processLink base l = - if any (l `hasUriScheme`) ["http", "https", "ftp", "mailto"] - || take 1 l == "#" - then pure l - else if isAbsolute l - then let (path, rest) = break (`elem` ['?', '#']) l - in (<> rest) <$> rootedPageLink path - else installFile (base l) >>= rootedLink +processLink :: FilePath -> FilePath -> FilePath -> Site String +processLink base mount l + | any (l `hasUriScheme`) ["http", "https", "ftp", "mailto"] || take 1 l == "#" = + pure l + | isAbsolute l = + let (path, rest) = break (`elem` ['?', '#']) l + in (<> rest) <$> rootedPageLink path + | l `hasUriScheme` "mount" = + let (path, rest) = break (`elem` ['?', '#']) $ drop 6 l + mountpath = + joinPath . reverse + $ foldl interpretPath (reverse $ splitPath mount) (splitPath path) + interpretPath m x + | x `elem` ["..", "../"] = drop 1 m + | x `elem` [".", "./"] = m + | x == "/" = ["/"] + | otherwise = x : m + in (<> rest) <$> rootedPageLink mountpath + | otherwise = installFile (base l) >>= rootedLink -- | Conjure a function that finds a displayable name for a page at a particular mount. pageName' :: Site (FilePath -> String) @@ -290,7 +301,8 @@ installPage mount pi = do templ <- use $ templates . to (M.! fromString tname) file <- indexFilename mount fixedUrlDoc <- - walkURLs (processLink $ pi ^. pagePath . to takeDirectory) $ pi ^. pageDoc + walkURLs (processLink (pi ^. pagePath . to takeDirectory) mount) + $ pi ^. pageDoc checkTarget file body <- io . runIOorExplode From 5fabdce6b81427c99bcc17b0f75e3f4dadd2194c Mon Sep 17 00:00:00 2001 From: Mirek Kratochvil Date: Wed, 29 Jan 2025 20:36:41 +0100 Subject: [PATCH 5/7] fix entrypoint a bit --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 4a35c65..9dbe0ea 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,4 +31,4 @@ WORKDIR /data COPY assets /data/assets/ COPY templates /data/templates/ COPY pages /data/pages/ -ENTRYPOINT ["/root/.cabal/bin/reploy"] +ENTRYPOINT ["/root/.local/bin/reploy"] From 859a65e61d9dc6cd68f048fda0f0da4b9bcc1396 Mon Sep 17 00:00:00 2001 From: Mirek Kratochvil Date: Wed, 29 Jan 2025 20:37:50 +0100 Subject: [PATCH 6/7] extra metadata loading from a yaml file --- Types.hs | 40 ++++++++++++++++++++++++---------------- reploy.hs | 28 ++++++++++++++++++++++++++++ templates/header.html | 6 ++++++ test.yml | 3 +++ 4 files changed, 61 insertions(+), 16 deletions(-) create mode 100644 test.yml diff --git a/Types.hs b/Types.hs index e42dafa..78f0013 100644 --- a/Types.hs +++ b/Types.hs @@ -20,9 +20,7 @@ -- | Separated-out main types of the deployment scriptage. module Types where -import AesonUtils import Control.Monad.Trans.State.Lazy -import qualified Data.ByteString.UTF8 import qualified Data.Map as M import qualified Data.Set as S import qualified Data.Yaml as Y @@ -40,6 +38,14 @@ data PageInfo = PageInfo makeLenses ''PageInfo +-- | Information about where to source all extra metadata +data MetaSpec + = MetaSpecInline String + | MetaSpecFile FilePath + deriving (Show) + +makeLenses ''MetaSpec + -- | Complete internal state of the deployment process that holds all data data SiteState = SiteState { _pages :: M.Map FilePath PageInfo -- ^ Map of page mounts to `PageInfo` @@ -62,6 +68,7 @@ data SiteState = SiteState , _tagTemplate :: FilePath -- ^ Name of the template for category pages , _listTemplate :: FilePath -- ^ Name of the template for listing pages , _extraMeta :: Y.Value -- ^ Extra metadata added to rendering of all templates + , _extraMetaSpec :: [MetaSpec] -- ^ sources for the extra metadata , _metadataSuffix :: FilePath -- ^ File suffix to search for a extra metadata (e.g., if the suffix is ".extra", the extra metadata for file "page.md" will be looked for in "page.md.extra"). These are best autogenerated with a script that sources the data from git or so. , _indexFile :: FilePath -- ^ Name of the "index" files to be generated. , _urlBase :: FilePath -- ^ "Root route" to prepend to all absolute links. @@ -151,20 +158,20 @@ siteOptions' = do "Suffix for YAML files with base metadata for each markdown page. Metadata from files override the extra metadata specified on commandline, but are overridden by metadata specified directly in the markdown header of the pages." <> value ".metadata.yml" <> showDefault - _extraMeta <- - let processKeyVal :: String -> Y.Value - processKeyVal opt = - case Y.decodeEither' $ Data.ByteString.UTF8.fromString opt of - Right v -> v - Left err -> - error - $ "cannot parse YAML in --extra-metadata: " - ++ Y.prettyPrintParseException err - in fmap (foldl objMerge Y.Null . map processKeyVal) . many . strOption - $ long "extra-metadata" - <> short 'e' - <> help - "Extra metadata to add to pages rendering in YAML format. May be specified multiple times." + _extraMetaSpec <- + many + $ asum + [ fmap MetaSpecInline . strOption + $ long "extra-metadata" + <> short 'e' + <> help + "Extra metadata to add to pages rendering in YAML format. May be specified multiple times." + , fmap MetaSpecFile . strOption + $ long "extra-metadata-file" + <> short 'E' + <> help + "Extra metadata to add to pages rendering, loaded from a YAML file. May be specified multiple times." + ] _urlBase <- strOption $ long "url-base" @@ -199,6 +206,7 @@ siteOptions' = do , _installs = S.empty , _targets = S.empty , _templates = M.empty + , _extraMeta = Y.Null , .. } diff --git a/reploy.hs b/reploy.hs index dbdb091..cf4de56 100644 --- a/reploy.hs +++ b/reploy.hs @@ -26,6 +26,7 @@ import qualified Data.Aeson as AE import qualified Data.Aeson.Key as K import qualified Data.Aeson.KeyMap as KM import qualified Data.ByteString.Lazy as B +import qualified Data.ByteString.UTF8 import Data.Digest.Pure.SHA (sha256, showDigest) import Data.Foldable (traverse_) import Data.List (inits, nub, sort) @@ -511,10 +512,37 @@ renderSearchData = use searchDataOut >>= traverse_ go putStrLn $ "S -> " ++ out AE.encodeFile out $ Y.array (concat ps) +-- | Souce extra metadata accordingly to the metadata specifications +sourceExtraMetadata :: Site () +sourceExtraMetadata = do + use extraMetaSpec + >>= fmap (foldl objMerge Y.Null) . traverse loadSpec + >>= assign extraMeta + where + loadSpec :: MetaSpec -> Site Y.Value + loadSpec (MetaSpecInline yaml) = + case Y.decodeEither' $ Data.ByteString.UTF8.fromString yaml of + Right v -> pure v + Left err -> + error + $ "cannot parse extra metadata from inline YAML: " + ++ Y.prettyPrintParseException err + loadSpec (MetaSpecFile path) = do + res <- io $ Y.decodeFileEither path + case res of + Right v -> pure v + Left err -> + error + $ "cannot load YAML metadata from " + ++ path + ++ ": " + ++ Y.prettyPrintParseException err + -- | Build the whole site. main = do init <- Options.Applicative.execParser siteOptions flip runStateT init $ do + sourceExtraMetadata installAssets use sourceDirs >>= traverse sourcePages use sourceDirs >>= traverse sourceTagMeta diff --git a/templates/header.html b/templates/header.html index d4e4799..2eb2531 100644 --- a/templates/header.html +++ b/templates/header.html @@ -1,4 +1,10 @@
+ {{?menu_items}}Menu: +
    + {{#menu_items}}
  • {{.}}
  • {{/menu_items}} +
+ {{/menu_items}} + Navigation:
  • {{#pageName}}/{{/pageName}}
  • diff --git a/test.yml b/test.yml new file mode 100644 index 0000000..0ae8131 --- /dev/null +++ b/test.yml @@ -0,0 +1,3 @@ +menu_items: + - item1 + - item2 From f0258edf003fd58fe6df6b858bbccbc0efd70e08 Mon Sep 17 00:00:00 2001 From: Mirek Kratochvil Date: Wed, 29 Jan 2025 20:38:00 +0100 Subject: [PATCH 7/7] fix dependencies, bump version, reformat --- reploy.cabal | 73 ++++++++++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/reploy.cabal b/reploy.cabal index d49776c..d4d5f57 100644 --- a/reploy.cabal +++ b/reploy.cabal @@ -1,38 +1,45 @@ -cabal-version: 3.0 - -name: reploy -synopsis: Straightforward static all-in-one website builder -category: Web -version: 0.3.2.0 -build-type: Simple -license: Apache-2.0 -license-file: LICENSE +cabal-version: 3.0 +name: reploy +synopsis: Straightforward static all-in-one website builder +category: Web +version: 0.3.3.0 +build-type: Simple +license: Apache-2.0 +license-file: LICENSE executable reploy main-is: reploy.hs - other-modules: Types, AesonUtils, Utils, Tags, FormatOpts - build-depends: base == 4.* - , aeson ^>= 2.1 - , bytestring - , containers - , data-default - , directory - , extra - , filepath - , microlens - , microlens-aeson - , microlens-mtl - , microlens-th - , mustache - , optparse-applicative - , pandoc - , pandoc-types - , parsec - , scientific - , SHA - , text - , transformers - , utf8-string - , yaml + other-modules: + AesonUtils + FormatOpts + Tags + Types + Utils + + build-depends: + , aeson ^>=2.1 || ^>=2.2 + , base >=4 && <5 + , bytestring + , containers + , data-default + , directory + , extra + , filepath + , microlens + , microlens-aeson + , microlens-mtl + , microlens-th + , mustache + , optparse-applicative + , pandoc + , pandoc-types + , parsec + , scientific + , SHA + , text + , transformers + , utf8-string + , yaml + ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wunused-imports default-language: Haskell2010