mirror of
https://github.com/dillonkearns/elm-pages-v3-beta.git
synced 2024-12-23 20:03:31 +03:00
239 lines
9.7 KiB
Elm
239 lines
9.7 KiB
Elm
module MarkdownCodec exposing (isPlaceholder, noteTitle, titleAndDescription, withFrontmatter, withoutFrontmatter)
|
|
|
|
import BackendTask exposing (BackendTask)
|
|
import BackendTask.File as StaticFile
|
|
import FatalError exposing (FatalError)
|
|
import Json.Decode as Decode exposing (Decoder)
|
|
import Json.Decode.Extra
|
|
import List.Extra
|
|
import Markdown.Block as Block exposing (Block)
|
|
import Markdown.Parser
|
|
import Markdown.Renderer
|
|
import MarkdownExtra
|
|
|
|
|
|
isPlaceholder : String -> BackendTask FatalError (Maybe ())
|
|
isPlaceholder filePath =
|
|
filePath
|
|
|> StaticFile.bodyWithoutFrontmatter
|
|
|> BackendTask.allowFatal
|
|
|> BackendTask.andThen
|
|
(\rawContent ->
|
|
Markdown.Parser.parse rawContent
|
|
|> Result.mapError (\_ -> FatalError.fromString "Markdown error")
|
|
|> Result.map
|
|
(\blocks ->
|
|
List.any
|
|
(\block ->
|
|
case block of
|
|
Block.Heading _ inlines ->
|
|
False
|
|
|
|
_ ->
|
|
True
|
|
)
|
|
blocks
|
|
|> not
|
|
)
|
|
|> BackendTask.fromResult
|
|
)
|
|
|> BackendTask.map
|
|
(\bool ->
|
|
if bool then
|
|
Nothing
|
|
|
|
else
|
|
Just ()
|
|
)
|
|
|
|
|
|
noteTitle : String -> BackendTask FatalError String
|
|
noteTitle filePath =
|
|
titleFromFrontmatter filePath
|
|
|> BackendTask.andThen
|
|
(\maybeTitle ->
|
|
maybeTitle
|
|
|> Maybe.map BackendTask.succeed
|
|
|> Maybe.withDefault
|
|
(StaticFile.bodyWithoutFrontmatter filePath
|
|
|> BackendTask.allowFatal
|
|
|> 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
|
|
)
|
|
|> Result.andThen
|
|
(Result.fromMaybe <|
|
|
("Expected to find an H1 heading for page " ++ filePath)
|
|
)
|
|
|> Result.mapError FatalError.fromString
|
|
|> BackendTask.fromResult
|
|
)
|
|
)
|
|
)
|
|
|
|
|
|
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
|
|
|> BackendTask.andThen
|
|
(\metadata ->
|
|
Maybe.map2 (\title description -> { title = title, description = description })
|
|
metadata.title
|
|
metadata.description
|
|
|> Maybe.map BackendTask.succeed
|
|
|> Maybe.withDefault
|
|
(StaticFile.bodyWithoutFrontmatter filePath
|
|
|> BackendTask.allowFatal
|
|
|> 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)
|
|
|> Result.mapError FatalError.fromString
|
|
|> 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
|
|
)
|
|
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 ""
|
|
|
|
|
|
titleFromFrontmatter : String -> BackendTask FatalError (Maybe String)
|
|
titleFromFrontmatter filePath =
|
|
StaticFile.onlyFrontmatter
|
|
(Json.Decode.Extra.optionalField "title" Decode.string)
|
|
filePath
|
|
|> BackendTask.allowFatal
|
|
|
|
|
|
withoutFrontmatter :
|
|
Markdown.Renderer.Renderer view
|
|
-> String
|
|
-> BackendTask FatalError (List Block)
|
|
withoutFrontmatter renderer filePath =
|
|
(filePath
|
|
|> StaticFile.bodyWithoutFrontmatter
|
|
|> BackendTask.allowFatal
|
|
|> BackendTask.andThen
|
|
(\rawBody ->
|
|
rawBody
|
|
|> Markdown.Parser.parse
|
|
|> Result.mapError (\_ -> FatalError.fromString "Couldn't parse markdown.")
|
|
|> BackendTask.fromResult
|
|
)
|
|
)
|
|
|> 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)
|
|
|> Result.mapError (\error -> FatalError.fromString error)
|
|
|> BackendTask.fromResult
|
|
)
|
|
|
|
|
|
withFrontmatter :
|
|
(frontmatter -> List Block -> value)
|
|
-> Decoder frontmatter
|
|
-> Markdown.Renderer.Renderer view
|
|
-> String
|
|
-> BackendTask FatalError value
|
|
withFrontmatter constructor frontmatterDecoder_ renderer filePath =
|
|
BackendTask.map2 constructor
|
|
(StaticFile.onlyFrontmatter
|
|
frontmatterDecoder_
|
|
filePath
|
|
|> BackendTask.allowFatal
|
|
)
|
|
(StaticFile.bodyWithoutFrontmatter
|
|
filePath
|
|
|> BackendTask.allowFatal
|
|
|> BackendTask.andThen
|
|
(\rawBody ->
|
|
rawBody
|
|
|> Markdown.Parser.parse
|
|
|> Result.mapError (\_ -> FatalError.fromString "Couldn't parse markdown.")
|
|
|> BackendTask.fromResult
|
|
)
|
|
|> 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)
|
|
|> Result.mapError (\error -> FatalError.fromString error)
|
|
|> BackendTask.fromResult
|
|
)
|
|
)
|