elm-pages-v3-beta/plugins/MarkdownCodec.elm

239 lines
9.7 KiB
Elm
Raw Normal View History

2021-08-11 23:03:52 +03:00
module MarkdownCodec exposing (isPlaceholder, noteTitle, titleAndDescription, withFrontmatter, withoutFrontmatter)
2021-06-18 19:01:03 +03:00
2023-01-02 01:44:11 +03:00
import BackendTask exposing (BackendTask)
import BackendTask.File as StaticFile
2023-01-17 02:22:44 +03:00
import FatalError exposing (FatalError)
import Json.Decode as Decode exposing (Decoder)
import Json.Decode.Extra
import List.Extra
2021-06-18 19:01:03 +03:00
import Markdown.Block as Block exposing (Block)
import Markdown.Parser
import Markdown.Renderer
import MarkdownExtra
2021-06-18 19:01:03 +03:00
2023-01-17 02:22:44 +03:00
isPlaceholder : String -> BackendTask FatalError (Maybe ())
2021-08-11 23:03:52 +03:00
isPlaceholder filePath =
filePath
|> StaticFile.bodyWithoutFrontmatter
|> BackendTask.allowFatal
2023-01-02 01:44:11 +03:00
|> BackendTask.andThen
2021-08-11 23:03:52 +03:00
(\rawContent ->
Markdown.Parser.parse rawContent
2023-01-17 02:22:44 +03:00
|> Result.mapError (\_ -> FatalError.fromString "Markdown error")
2021-08-11 23:03:52 +03:00
|> Result.map
(\blocks ->
List.any
(\block ->
case block of
Block.Heading _ inlines ->
False
_ ->
True
)
blocks
|> not
)
2023-01-02 01:44:11 +03:00
|> BackendTask.fromResult
2021-08-11 23:03:52 +03:00
)
2023-01-02 01:44:11 +03:00
|> BackendTask.map
2021-08-11 23:03:52 +03:00
(\bool ->
if bool then
Nothing
else
Just ()
)
2023-01-17 02:22:44 +03:00
noteTitle : String -> BackendTask FatalError String
noteTitle filePath =
titleFromFrontmatter filePath
2023-01-02 01:44:11 +03:00
|> BackendTask.andThen
(\maybeTitle ->
maybeTitle
2023-01-02 01:44:11 +03:00
|> Maybe.map BackendTask.succeed
|> Maybe.withDefault
(StaticFile.bodyWithoutFrontmatter filePath
|> BackendTask.allowFatal
2023-01-02 01:44:11 +03:00
|> BackendTask.andThen
(\rawContent ->
Markdown.Parser.parse rawContent
|> Result.mapError (\_ -> "Markdown error")
|> Result.map
(\blocks ->
List.Extra.findMap
(\block ->
case block of
Block.Heading Block.H1 inlines ->
Just (Block.extractInlineText inlines)
_ ->
Nothing
)
blocks
)
2022-12-31 04:06:57 +03:00
|> Result.andThen
(Result.fromMaybe <|
("Expected to find an H1 heading for page " ++ filePath)
)
2023-01-17 02:22:44 +03:00
|> Result.mapError FatalError.fromString
2023-01-02 01:44:11 +03:00
|> BackendTask.fromResult
)
)
)
2023-01-17 02:22:44 +03:00
titleAndDescription : String -> BackendTask FatalError { title : String, description : String }
titleAndDescription filePath =
filePath
|> StaticFile.onlyFrontmatter
(Decode.map2 (\title description -> { title = title, description = description })
(Json.Decode.Extra.optionalField "title" Decode.string)
(Json.Decode.Extra.optionalField "description" Decode.string)
)
|> BackendTask.allowFatal
2023-01-02 01:44:11 +03:00
|> BackendTask.andThen
(\metadata ->
Maybe.map2 (\title description -> { title = title, description = description })
metadata.title
metadata.description
2023-01-02 01:44:11 +03:00
|> Maybe.map BackendTask.succeed
|> Maybe.withDefault
(StaticFile.bodyWithoutFrontmatter filePath
|> BackendTask.allowFatal
2023-01-02 01:44:11 +03:00
|> BackendTask.andThen
(\rawContent ->
Markdown.Parser.parse rawContent
|> Result.mapError (\_ -> "Markdown error")
|> Result.map
(\blocks ->
Maybe.map
(\title ->
{ title = title
, description =
case metadata.description of
Just description ->
description
Nothing ->
findDescription blocks
}
)
(case metadata.title of
Just title ->
Just title
Nothing ->
findH1 blocks
)
)
|> Result.andThen (Result.fromMaybe <| "Expected to find an H1 heading for page " ++ filePath)
2023-01-17 02:22:44 +03:00
|> Result.mapError FatalError.fromString
2023-01-02 01:44:11 +03:00
|> BackendTask.fromResult
)
)
)
findH1 : List Block -> Maybe String
findH1 blocks =
List.Extra.findMap
(\block ->
case block of
Block.Heading Block.H1 inlines ->
Just (Block.extractInlineText inlines)
_ ->
Nothing
2021-06-23 08:14:48 +03:00
)
blocks
findDescription : List Block -> String
findDescription blocks =
blocks
|> List.Extra.findMap
(\block ->
case block of
Block.Paragraph inlines ->
Just (MarkdownExtra.extractInlineText inlines)
_ ->
Nothing
)
|> Maybe.withDefault ""
2023-01-17 02:22:44 +03:00
titleFromFrontmatter : String -> BackendTask FatalError (Maybe String)
titleFromFrontmatter filePath =
StaticFile.onlyFrontmatter
(Json.Decode.Extra.optionalField "title" Decode.string)
filePath
|> BackendTask.allowFatal
2021-06-23 08:14:48 +03:00
withoutFrontmatter :
Markdown.Renderer.Renderer view
-> String
2023-01-17 02:22:44 +03:00
-> BackendTask FatalError (List Block)
withoutFrontmatter renderer filePath =
2021-08-11 23:03:52 +03:00
(filePath
|> StaticFile.bodyWithoutFrontmatter
|> BackendTask.allowFatal
2023-01-02 01:44:11 +03:00
|> BackendTask.andThen
(\rawBody ->
rawBody
|> Markdown.Parser.parse
2023-01-17 02:22:44 +03:00
|> Result.mapError (\_ -> FatalError.fromString "Couldn't parse markdown.")
2023-01-02 01:44:11 +03:00
|> BackendTask.fromResult
)
)
2023-01-02 01:44:11 +03:00
|> BackendTask.andThen
(\blocks ->
blocks
|> Markdown.Renderer.render renderer
-- we don't want to encode the HTML since it contains functions so it's not serializable
-- but we can at least make sure there are no errors turning it into HTML before encoding it
|> Result.map (\_ -> blocks)
2023-01-17 02:22:44 +03:00
|> Result.mapError (\error -> FatalError.fromString error)
2023-01-02 01:44:11 +03:00
|> BackendTask.fromResult
)
withFrontmatter :
(frontmatter -> List Block -> value)
2021-06-23 08:14:48 +03:00
-> Decoder frontmatter
-> Markdown.Renderer.Renderer view
-> String
2023-01-17 02:22:44 +03:00
-> BackendTask FatalError value
withFrontmatter constructor frontmatterDecoder_ renderer filePath =
2023-01-02 01:44:11 +03:00
BackendTask.map2 constructor
(StaticFile.onlyFrontmatter
frontmatterDecoder_
filePath
|> BackendTask.allowFatal
)
(StaticFile.bodyWithoutFrontmatter
filePath
|> BackendTask.allowFatal
2023-01-02 01:44:11 +03:00
|> BackendTask.andThen
(\rawBody ->
rawBody
|> Markdown.Parser.parse
2023-01-17 02:22:44 +03:00
|> Result.mapError (\_ -> FatalError.fromString "Couldn't parse markdown.")
2023-01-02 01:44:11 +03:00
|> BackendTask.fromResult
)
2023-01-02 01:44:11 +03:00
|> BackendTask.andThen
(\blocks ->
blocks
|> Markdown.Renderer.render renderer
-- we don't want to encode the HTML since it contains functions so it's not serializable
-- but we can at least make sure there are no errors turning it into HTML before encoding it
|> Result.map (\_ -> blocks)
2023-01-17 02:22:44 +03:00
|> Result.mapError (\error -> FatalError.fromString error)
2023-01-02 01:44:11 +03:00
|> BackendTask.fromResult
)
)