stache 2.2.1 → 2.3.0
raw patch · 10 files changed
+79/−73 lines, 10 filesdep ~optparse-applicative
Dependency ranges changed: optparse-applicative
Files
- CHANGELOG.md +5/−0
- README.md +9/−11
- Text/Mustache.hs +10/−12
- Text/Mustache/Compile.hs +13/−12
- Text/Mustache/Compile/TH.hs +7/−13
- Text/Mustache/Parser.hs +10/−5
- Text/Mustache/Render.hs +3/−3
- Text/Mustache/Type.hs +8/−8
- stache.cabal +5/−5
- tests/Text/Mustache/ParserSpec.hs +9/−4
CHANGELOG.md view
@@ -1,3 +1,8 @@+## Stache 2.3.0++* Allowed parsing a wider range of keys in the templates thus making the+ library's behavior closer to other existing implementations.+ ## Stache 2.2.1 * Works with GHC 9.0.1, dropped support for GHC 8.6.x and older.
README.md view
@@ -8,22 +8,20 @@ This is a Haskell implementation of Mustache templates. The implementation conforms to the version 1.1.3 of the official [Mustache-specification](https://github.com/mustache/spec). It is extremely simple and-straightforward to use with minimal but complete API—three functions to-compile templates (from directory, from file, and from lazy text) and one to-render them.+specification](https://github.com/mustache/spec). It has a minimal but+complete API—three functions to compile templates (from directory, from+file, and from lazy text) and one to render them. The implementation uses the Megaparsec parsing library to parse the-templates which results in superior quality of error messages.+templates which results in high-quality error messages. -For rendering you only need to create Aeson's `Value` where you put the data-to interpolate. Since the library re-uses Aeson's instances and most data-types in the Haskell ecosystem are instances of classes like-`Data.Aeson.ToJSON`, the whole process is very simple for the end user.+For rendering one only needs to create Aeson's `Value` that is used for+interpolation of template variables. Since the library re-uses Aeson's+instances and most data types in the Haskell ecosystem are instances of+classes like `Data.Aeson.ToJSON`, the process is simple for the end user. Template Haskell helpers for compilation of templates at compile time are-available in the `Text.Mustache.Compile.TH` module. The helpers currently-work only with GHC 8 and later.+available in the `Text.Mustache.Compile.TH` module. One feature that is not currently supported is lambdas. The feature is marked as optional in the spec and can be emulated via processing of parsed
Text/Mustache.hs view
@@ -8,24 +8,22 @@ -- Portability : portable -- -- This is a Haskell implementation of Mustache templates. The--- implementation conforms to the version 1.1.3 of official Mustache--- specification <https://github.com/mustache/spec>. It is extremely simple--- and straightforward to use with minimal but complete API—three functions--- to compile templates (from directory, from file, and from lazy text) and--- one to render them.+-- implementation conforms to the version 1.1.3 of the official Mustache+-- specification <https://github.com/mustache/spec>. It has a minimal but+-- complete API—three functions to compile templates (from directory, from+-- file, and from lazy text) and one to render them. -- -- The implementation uses Megaparsec parsing library to parse the templates--- which results in superior quality of error messages.+-- which results in high-quality error messages. ----- For rendering you only need to create Aeson's 'Data.Aeson.Value' where--- you put the data to interpolate. Since the library re-uses Aeson's--- instances and most data types in the Haskell ecosystem are instances of--- classes like 'Data.Aeson.ToJSON', the whole process is very simple for+-- For rendering you only need to create Aeson's 'Data.Aeson.Value' that is+-- used for interpolation of template variables. Since the library re-uses+-- Aeson's instances and most data types in the Haskell ecosystem are+-- instances of classes like 'Data.Aeson.ToJSON', the process is simple for -- the end user. -- -- Template Haskell helpers for compilation of templates at compile time are--- available in the "Text.Mustache.Compile.TH" module. The helpers currently--- work only with GHC 8 and later.+-- available in the "Text.Mustache.Compile.TH" module. -- -- One feature that is not currently supported is lambdas. The feature is -- marked as optional in the spec and can be emulated via processing of
Text/Mustache/Compile.hs view
@@ -34,9 +34,10 @@ import Text.Mustache.Parser import Text.Mustache.Type --- | Compile all templates in specified directory and select one. Template--- files should have the extension @mustache@, (e.g. @foo.mustache@) to be--- recognized. This function /does not/ scan the directory recursively.+-- | Compile all templates in the specified directory and select one.+-- Template files should have the extension @mustache@, (e.g.+-- @foo.mustache@) to be recognized. This function /does not/ scan the+-- directory recursively. -- -- Note that each template\/partial will get an identifier which consists of -- the name of corresponding template file with extension @.mustache@@@ -80,8 +81,8 @@ Template _ new <- compileMustacheFile fp return (Template undefined (M.union new old)) --- | Return a list of templates found in given directory. The returned paths--- are absolute.+-- | Return a list of templates found in given a directory. The returned+-- paths are absolute. -- -- @since 0.2.2 getMustacheFilesInDir ::@@ -91,8 +92,8 @@ m [FilePath] getMustacheFilesInDir = getMustacheFilesInDir' isMustacheFile --- | Return a list of templates found via a predicate in given directory.--- The returned paths are absolute.+-- | Return a list of templates that satisfy a predicate in a given+-- directory. The returned paths are absolute. -- -- @since 1.2.0 getMustacheFilesInDir' ::@@ -116,7 +117,7 @@ isMustacheFile :: FilePath -> Bool isMustacheFile path = F.takeExtension path == ".mustache" --- | Compile a single Mustache template and select it.+-- | Compile a Mustache template and select it. -- -- The action can throw 'MustacheParserException' and the same exceptions as -- 'T.readFile'.@@ -132,7 +133,7 @@ pname = pathToPName path compile = fmap (Template pname . M.singleton pname) . parseMustache path --- | Compile Mustache template from a lazy 'Text' value. The cache will+-- | Compile a Mustache template from a lazy 'Text' value. The cache will -- contain only this template named according to given 'PName'. compileMustacheText :: -- | How to name the template?@@ -147,12 +148,12 @@ ---------------------------------------------------------------------------- -- Helpers --- | Build a 'PName' from given 'FilePath'.+-- | Build a 'PName' from a given 'FilePath'. pathToPName :: FilePath -> PName pathToPName = PName . T.pack . F.takeBaseName --- | Throw 'MustacheException' if argument is 'Left' or return the result--- inside 'Right'.+-- | Throw 'MustacheException' if the argument is 'Left' or return the+-- result inside 'Right'. withException :: -- | Value to process Either (ParseErrorBundle Text Void) Template ->
Text/Mustache/Compile/TH.hs view
@@ -14,9 +14,6 @@ -- Template Haskell helpers to compile Mustache templates at compile time. -- This module is not imported as part of "Text.Mustache", so you need to -- import it yourself. Qualified import is recommended, but not necessary.------ At the moment, functions in this module only work with GHC 8 (they--- require at least @template-haskell-2.11@). module Text.Mustache.Compile.TH ( compileMustacheDir, compileMustacheDir',@@ -36,9 +33,10 @@ import qualified Text.Mustache.Compile as C import Text.Mustache.Type --- | Compile all templates in specified directory and select one. Template--- files should have the extension @mustache@, (e.g. @foo.mustache@) to be--- recognized. This function /does not/ scan the directory recursively.+-- | Compile all templates in the specified directory and select one.+-- Template files should have the extension @mustache@, (e.g.+-- @foo.mustache@) to be recognized. This function /does not/ scan the+-- directory recursively. -- -- This version compiles the templates at compile time. --@@ -71,7 +69,7 @@ runIO (C.getMustacheFilesInDir' predicate path) >>= mapM_ addDependentFile (runIO . try) (C.compileMustacheDir' predicate pname path) >>= handleEither --- | Compile single Mustache template and select it.+-- | Compile a Mustache template and select it. -- -- This version compiles the template at compile time. compileMustacheFile ::@@ -82,7 +80,7 @@ runIO (makeAbsolute path) >>= addDependentFile (runIO . try) (C.compileMustacheFile path) >>= handleEither --- | Compile Mustache template from 'Text' value. The cache will contain+-- | Compile a Mustache template from 'Text' value. The cache will contain -- only this template named according to given 'Key'. -- -- This version compiles the template at compile time.@@ -96,7 +94,7 @@ (handleEither . either (Left . MustacheParserException) Right) (C.compileMustacheText pname text) --- | Compile Mustache using QuasiQuoter. Usage:+-- | Compile a Mustache using a QuasiQuoter. Usage: -- -- > {-# LANGUAGE QuasiQuotes #-} -- > import Text.Mustache.Compile.TH (mustache)@@ -126,10 +124,6 @@ Left err -> (fail . indentNicely . displayException) err Right template -> lift template where- -- NOTE Since the feature requires GHC 8 anyway, we follow the- -- indentation style of that version of compiler. This makes it look- -- consistent with other error messages and allows Emacs and similar- -- tools to parse the errors correctly. indentNicely x' = case lines x' of [] -> ""
Text/Mustache/Parser.hs view
@@ -19,9 +19,9 @@ import Control.Monad import Control.Monad.State.Strict-import Data.Char (isAlphaNum, isSpace)+import Data.Char (isSpace) import Data.Maybe (catMaybes)-import Data.Text (Text)+import Data.Text (Text, stripEnd) import qualified Data.Text as T import Data.Void import Text.Megaparsec@@ -155,9 +155,14 @@ pKey = (fmap Key . lexeme . label "key") (implicit <|> other) where implicit = [] <$ char '.'- other = sepBy1 (takeWhile1P (Just lbl) f) (char '.')- lbl = "alphanumeric char or '-' or '_'"- f x = isAlphaNum x || x == '-' || x == '_'+ other = do+ end <- gets closingDel+ let f x = x `notElem` ('.' : '}' : T.unpack end)+ lbl = "key-constituent characters"+ stripLast <$> sepBy1 (takeWhile1P (Just lbl) f) (char '.')+ stripLast [] = []+ stripLast [x] = [stripEnd x]+ stripLast (x0 : x1 : xs) = x0 : stripLast (x1 : xs) {-# INLINE pKey #-} pDelimiter :: Parser Text
Text/Mustache/Render.hs view
@@ -39,9 +39,9 @@ ---------------------------------------------------------------------------- -- The rendering monad --- | Synonym for the monad we use for rendering. It allows to share context--- and accumulate the result as 'B.Builder' data which is then turned into a--- lazy 'TL.Text'.+-- | Synonym for the monad we use for rendering. It allows us to share+-- context and accumulate the result as 'B.Builder' data which is then+-- turned into a lazy 'TL.Text'. type Render a = ReaderT RenderContext (State S) a data S = S ([MustacheWarning] -> [MustacheWarning]) B.Builder
Text/Mustache/Type.hs view
@@ -43,17 +43,17 @@ import qualified Language.Haskell.TH.Syntax as TH import Text.Megaparsec --- | Mustache template as name of “top-level” template and a collection of--- all available templates (partials).+-- | Mustache template as the name of the “top-level” template and a+-- collection of all available templates (partials). -- -- 'Template' is a 'Semigroup'. This means that you can combine 'Template's -- (and their caches) using the @('<>')@ operator, the resulting 'Template' -- will have the same currently selected template as the left one. Union of -- caches is also left-biased. data Template = Template- { -- | Name of currently “selected” template (top-level one).+ { -- | The name of the currently “selected” template. templateActual :: PName,- -- | Collection of all templates that are available for interpolation+ -- | A collection of all templates that are available for interpolation -- (as partials). The top-level one is also contained here and the -- “focus” can be switched easily by modifying 'templateActual'. templateCache :: Map PName [Node]@@ -73,7 +73,7 @@ liftTyped = TH.unsafeTExpCoerce . TH.lift #endif --- | Structural element of template.+-- | A structural element of a template. data Node = -- | Plain text contained between tags TextBlock Text@@ -121,7 +121,7 @@ liftTyped = TH.unsafeTExpCoerce . TH.lift #endif --- | Pretty-print a key, this is helpful, for example, if you want to+-- | Pretty-print a key. This is helpful, for example, if you want to -- display an error message. -- -- @since 0.2.0@@ -167,8 +167,8 @@ -- -- @since 1.1.1 data MustacheWarning- = -- | The template contained a variable for which there was no data- -- counterpart in the current context.+ = -- | The template contained a variable for which there was no data in+ -- the current context. MustacheVariableNotFound Key | -- | A complex value such as an 'Object' or 'Array' was directly -- rendered into the template.
stache.cabal view
@@ -1,6 +1,6 @@ cabal-version: 1.18 name: stache-version: 2.2.1+version: 2.3.0 license: BSD3 license-file: LICENSE.md maintainer: Mark Karpov <markkarpov92@gmail.com>@@ -109,7 +109,7 @@ hspec >=2.0 && <3.0, hspec-megaparsec >=2.0 && <3.0, megaparsec >=7.0 && <10.0,- stache -any,+ stache, template-haskell >=2.11 && <2.18, text >=1.2 && <1.3 @@ -129,10 +129,10 @@ base >=4.13 && <5.0, bytestring >=0.10 && <0.12, containers >=0.5 && <0.7,- file-embed -any,+ file-embed, hspec >=2.0 && <3.0, megaparsec >=7.0 && <10.0,- stache -any,+ stache, text >=1.2 && <1.3, yaml >=0.8 && <0.12 @@ -153,7 +153,7 @@ criterion >=0.6.2.1 && <1.6, deepseq >=1.4 && <1.5, megaparsec >=7.0 && <10.0,- stache -any,+ stache, text >=1.2 && <1.3 if flag(dev)
tests/Text/Mustache/ParserSpec.hs view
@@ -30,6 +30,10 @@ p "{{{ name }}}" `shouldParse` [UnescapedVar (key "name")] it "parses unescaped {{& variable }}" $ p "{{& name }}" `shouldParse` [UnescapedVar (key "name")]+ it "parses escaped {{ nested.variable }}" $+ p "{{ a.b }}" `shouldParse` [EscapedVar (Key ["a", "b"])]+ it "parses escaped {{ nested . variable . with . spaces }}" $+ p "{{ a . b . c }}" `shouldParse` [EscapedVar (Key ["a ", " b ", " c"])] context "without white space" $ do it "parses escaped {{variable}}" $ p "{{name}}" `shouldParse` [EscapedVar (key "name")]@@ -87,9 +91,10 @@ p "{{#section}}{{=<< >>=}}<</section>><<var>>" `shouldParse` [Section (key "section") [], EscapedVar (key "var")] context "when given malformed input" $ do+ let keyConstErr = "key-constituent characters" it "rejects unclosed tags" $ do let s = "{{ name "- p s `shouldFailWith` err 8 (ueof <> etoks "}}")- it "rejects unknown tags" $ do- let s = "{{? boo }}"- p s `shouldFailWith` err 2 (utoks "?" <> elabel "key")+ p s `shouldFailWith` err 8 (ueof <> etoks "}}" <> etok '.' <> elabel keyConstErr)+ it "rejects il-formed expressions" $ do+ let s = "{{ foo..bar }}"+ p s `shouldFailWith` err 7 (utoks "." <> elabel keyConstErr)