mirror of
https://github.com/srid/ema.git
synced 2024-11-29 09:25:14 +03:00
markdown helper gone
This commit is contained in:
parent
cabae54a23
commit
f65e2b1b73
@ -6,6 +6,7 @@
|
||||
- Helpers
|
||||
- `Ema.Helpers.PathTree` moved to separate package *pathtree*.
|
||||
- `Ema.Helpers.FileSystem` moved to separate package *unionmount*.
|
||||
- `Ema.Helpers.Markdown` moved to separate package *commonmark-simple*.
|
||||
|
||||
## 0.4.0.0 -- 2022-01-19
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
---
|
||||
order: 5
|
||||
---
|
||||
# Helpers
|
||||
# Howto
|
||||
|
||||
Beyond the model and route types, Ema leaves it up to you as to how to develop your site. The following are not *required* when using Ema; nevertheless they are useful inasmuch as they capture common patterns in writing a good static site:
|
||||
Beyond the model and route types, Ema leaves it up to you as to how to develop your site. Here are some common aspects of writing a static site:
|
||||
|
||||
* [Blaze](guide/helpers/blaze.md) -- [We recommend--but not mandate--Tailwind for CSS and blaze-html as HTML DSL]{.item-intro}
|
||||
* [Blaze HTML DSL](guide/helpers/blaze.md) -- [Use blaze-html with Tailwind CSS]{.item-intro}
|
||||
* [Working with files](guide/helpers/filesystem.md) -- [Use `unionmount` to support hot-reload on files]{.item-intro}
|
||||
* [Converting Markdown](guide/helpers/markdown.md) -- [Pointers on how to work with Markdown files]{.item-intro}
|
@ -8,14 +8,15 @@ There are quite a few packages to convert Markdown to HTML,
|
||||
|
||||
- [Pandoc](https://hackage.haskell.org/package/pandoc) -- [Supports formats other than Markdown]{.item-intro}
|
||||
- [commonmark-hs](https://github.com/jgm/commonmark-hs) -- [Lightweight parser by the same author of Pandoc]{.item-intro}
|
||||
- [commonmark-simple](https://hackage.haskell.org/package/commonmark-simple-0.1.0.0) -- [Simpler interface to the above, with frontmatter support]{.item-intro}
|
||||
- [mmark](https://github.com/mmark-md/mmark) -- [*Strict* Markdown parser]{.item-intro}
|
||||
|
||||
## Helper
|
||||
## `commonmark-simple`
|
||||
|
||||
Ema provides a helper to parse Markdown files with YAML frontmatter, using commonmark-hs. If you are parsing front matter, you can use any type that has a [`FromYAML`](https://hackage.haskell.org/package/HsYAML-0.2.1.0/docs/Data-YAML.html#t:FromYAML) instance.
|
||||
`commonmark-simple` uses commonmark-hs to provide a simpler API, along with front matter support. If you are parsing front matter, you can use any type that has a [`FromYAML`](https://hackage.haskell.org/package/HsYAML-0.2.1.0/docs/Data-YAML.html#t:FromYAML) instance.
|
||||
|
||||
```haskell
|
||||
import qualified Ema.Helper.Markdown as Markdown
|
||||
import qualified Commonmark.Simple as CS
|
||||
|
||||
-- Front matter metadata can be any type with a `FromYAML` instance
|
||||
--
|
||||
@ -24,10 +25,10 @@ import qualified Ema.Helper.Markdown as Markdown
|
||||
type Metadata = Map Text Text
|
||||
|
||||
-- Returns `Either Text (Metadata, Pandoc)`
|
||||
Markdown.parseMarkdownWithFrontMatter @Metadata
|
||||
CS.parseMarkdownWithFrontMatter @Metadata
|
||||
"test.md" "Hello *world*"
|
||||
```
|
||||
|
||||
The template repo, as well as [Emanote](https://github.com/srid/emanote) (used to generate this site), uses this helper to parse Markdown files into Pandoc AST. Consult [the template repo's source code](https://github.com/srid/ema-template/blob/master/src/Main.hs) for details.
|
||||
|
||||
Note that with Ema you can get [hot reload](concepts/hot-reload.md) support for your Markdown files using [filesystem notifications](guide/helpers/filesystem.md).
|
||||
Note that with Ema you can get [hot reload](concepts/hot-reload.md) support for your Markdown files using [the `unionmount` package](guide/helpers/filesystem.md).
|
||||
|
12
ema.cabal
12
ema.cabal
@ -1,6 +1,6 @@
|
||||
cabal-version: 2.4
|
||||
name: ema
|
||||
version: 0.5.2.0
|
||||
version: 0.5.3.0
|
||||
license: AGPL-3.0-only
|
||||
copyright: 2021 Sridhar Ratnakumar
|
||||
maintainer: srid@srid.ca
|
||||
@ -68,15 +68,6 @@ library
|
||||
build-depends:
|
||||
, blaze-html
|
||||
, blaze-markup
|
||||
, commonmark
|
||||
, commonmark-extensions
|
||||
, commonmark-pandoc
|
||||
, megaparsec
|
||||
, pandoc-types
|
||||
, parsec
|
||||
, parser-combinators
|
||||
, time
|
||||
, yaml
|
||||
|
||||
if flag(with-examples)
|
||||
build-depends: time
|
||||
@ -139,7 +130,6 @@ library
|
||||
if (flag(with-helpers) || flag(with-examples))
|
||||
exposed-modules:
|
||||
Ema.Helper.Blaze
|
||||
Ema.Helper.Markdown
|
||||
|
||||
other-modules:
|
||||
Ema.App
|
||||
|
@ -1,132 +0,0 @@
|
||||
-- | Helper to deal with Markdown files
|
||||
--
|
||||
-- TODO: Publish this eventually to Hackage, along with wiki-link stuff from
|
||||
-- emanote (maybe as separate package).
|
||||
module Ema.Helper.Markdown
|
||||
( -- Parsing
|
||||
-- TODO: Publish to Hackage as commonmark-pandoc-simple?
|
||||
parseMarkdownWithFrontMatter,
|
||||
parseMarkdown,
|
||||
fullMarkdownSpec,
|
||||
-- Utilities
|
||||
plainify,
|
||||
)
|
||||
where
|
||||
|
||||
import Commonmark qualified as CM
|
||||
import Commonmark.Extensions qualified as CE
|
||||
import Commonmark.Pandoc qualified as CP
|
||||
import Control.Monad.Combinators (manyTill)
|
||||
import Data.Aeson (FromJSON)
|
||||
import Data.Yaml qualified as Y
|
||||
import Text.Megaparsec qualified as M
|
||||
import Text.Megaparsec.Char qualified as M
|
||||
import Text.Pandoc.Builder qualified as B
|
||||
import Text.Pandoc.Definition (Pandoc (..))
|
||||
import Text.Pandoc.Walk qualified as W
|
||||
|
||||
-- | Parse a Markdown file using commonmark-hs with all extensions enabled
|
||||
parseMarkdownWithFrontMatter ::
|
||||
forall meta m il bl.
|
||||
( FromJSON meta,
|
||||
m ~ Either CM.ParseError,
|
||||
bl ~ CP.Cm () B.Blocks,
|
||||
il ~ CP.Cm () B.Inlines
|
||||
) =>
|
||||
CM.SyntaxSpec m il bl ->
|
||||
-- | Path to file associated with this Markdown
|
||||
FilePath ->
|
||||
-- | Markdown text to parse
|
||||
Text ->
|
||||
Either Text (Maybe meta, Pandoc)
|
||||
parseMarkdownWithFrontMatter spec fn s = do
|
||||
(mMeta, markdown) <- partitionMarkdown fn s
|
||||
mMetaVal <- first show $ (Y.decodeEither' . encodeUtf8) `traverse` mMeta
|
||||
blocks <- first show $ join $ CM.commonmarkWith @(Either CM.ParseError) spec fn markdown
|
||||
let doc = Pandoc mempty $ B.toList . CP.unCm @() @B.Blocks $ blocks
|
||||
pure (mMetaVal, doc)
|
||||
|
||||
parseMarkdown :: FilePath -> Text -> Either Text Pandoc
|
||||
parseMarkdown fn s = do
|
||||
cmBlocks <- first show $ join $ CM.commonmarkWith @(Either CM.ParseError) fullMarkdownSpec fn s
|
||||
let blocks = B.toList . CP.unCm @() @B.Blocks $ cmBlocks
|
||||
pure $ Pandoc mempty blocks
|
||||
|
||||
type SyntaxSpec' m il bl =
|
||||
( Monad m,
|
||||
CM.IsBlock il bl,
|
||||
CM.IsInline il,
|
||||
Typeable m,
|
||||
Typeable il,
|
||||
Typeable bl,
|
||||
CE.HasEmoji il,
|
||||
CE.HasStrikethrough il,
|
||||
CE.HasPipeTable il bl,
|
||||
CE.HasTaskList il bl,
|
||||
CM.ToPlainText il,
|
||||
CE.HasFootnote il bl,
|
||||
CE.HasMath il,
|
||||
CE.HasDefinitionList il bl,
|
||||
CE.HasDiv bl,
|
||||
CE.HasQuoted il,
|
||||
CE.HasSpan il
|
||||
)
|
||||
|
||||
-- | GFM + official commonmark extensions
|
||||
fullMarkdownSpec ::
|
||||
SyntaxSpec' m il bl =>
|
||||
CM.SyntaxSpec m il bl
|
||||
fullMarkdownSpec =
|
||||
mconcat
|
||||
[ CE.gfmExtensions,
|
||||
CE.fancyListSpec,
|
||||
CE.footnoteSpec,
|
||||
CE.mathSpec,
|
||||
CE.smartPunctuationSpec,
|
||||
CE.definitionListSpec,
|
||||
CE.attributesSpec,
|
||||
CE.rawAttributeSpec,
|
||||
CE.fencedDivSpec,
|
||||
CE.bracketedSpanSpec,
|
||||
CE.autolinkSpec,
|
||||
CM.defaultSyntaxSpec,
|
||||
-- as the commonmark documentation states, pipeTableSpec should be placed after
|
||||
-- fancyListSpec and defaultSyntaxSpec to avoid bad results when parsing
|
||||
-- non-table lines
|
||||
CE.pipeTableSpec
|
||||
]
|
||||
|
||||
-- | Identify metadata block at the top, and split it from markdown body.
|
||||
--
|
||||
-- FIXME: https://github.com/srid/neuron/issues/175
|
||||
partitionMarkdown :: FilePath -> Text -> Either Text (Maybe Text, Text)
|
||||
partitionMarkdown =
|
||||
parse (M.try splitP <|> fmap (Nothing,) M.takeRest)
|
||||
where
|
||||
separatorP :: M.Parsec Void Text ()
|
||||
separatorP =
|
||||
void $ M.string "---" <* M.eol
|
||||
splitP :: M.Parsec Void Text (Maybe Text, Text)
|
||||
splitP = do
|
||||
separatorP
|
||||
a <- toText <$> manyTill M.anySingle (M.try $ M.eol *> separatorP)
|
||||
b <- M.takeRest
|
||||
pure (Just a, b)
|
||||
parse :: M.Parsec Void Text a -> String -> Text -> Either Text a
|
||||
parse p fn s =
|
||||
first (toText . M.errorBundlePretty) $
|
||||
M.parse (p <* M.eof) fn s
|
||||
|
||||
-- | Convert Pandoc AST inlines to raw text.
|
||||
plainify :: [B.Inline] -> Text
|
||||
plainify = W.query $ \case
|
||||
B.Str x -> x
|
||||
B.Code _attr x -> x
|
||||
B.Space -> " "
|
||||
B.SoftBreak -> " "
|
||||
B.LineBreak -> " "
|
||||
B.RawInline _fmt s -> s
|
||||
B.Math _mathTyp s -> s
|
||||
-- Ignore the rest of AST nodes, as they are recursively defined in terms of
|
||||
-- `Inline` which `W.query` will traverse again.
|
||||
_ -> ""
|
Loading…
Reference in New Issue
Block a user