2021-08-11 23:03:52 +03:00
|
|
|
module MarkdownCodec exposing (isPlaceholder, noteTitle, titleAndDescription, withFrontmatter, withoutFrontmatter)
|
2021-06-18 19:01:03 +03:00
|
|
|
|
2021-07-30 20:43:14 +03:00
|
|
|
import DataSource exposing (DataSource)
|
2021-06-20 18:18:56 +03:00
|
|
|
import DataSource.File as StaticFile
|
2022-12-29 04:45:49 +03:00
|
|
|
import Exception exposing (Throwable)
|
2022-01-26 20:47:18 +03:00
|
|
|
import Json.Decode as Decode exposing (Decoder)
|
|
|
|
import Json.Decode.Extra
|
2021-07-30 20:43:14 +03:00
|
|
|
import List.Extra
|
2021-06-18 19:01:03 +03:00
|
|
|
import Markdown.Block as Block exposing (Block)
|
2021-06-20 18:18:56 +03:00
|
|
|
import Markdown.Parser
|
|
|
|
import Markdown.Renderer
|
2021-10-26 20:24:41 +03:00
|
|
|
import MarkdownExtra
|
2021-06-18 19:01:03 +03:00
|
|
|
|
|
|
|
|
2022-12-29 04:45:49 +03:00
|
|
|
isPlaceholder : String -> DataSource Throwable (Maybe ())
|
2021-08-11 23:03:52 +03:00
|
|
|
isPlaceholder filePath =
|
|
|
|
filePath
|
|
|
|
|> StaticFile.bodyWithoutFrontmatter
|
2022-12-29 04:45:49 +03:00
|
|
|
|> DataSource.throw
|
2021-08-11 23:03:52 +03:00
|
|
|
|> DataSource.andThen
|
|
|
|
(\rawContent ->
|
|
|
|
Markdown.Parser.parse rawContent
|
2022-12-29 04:45:49 +03:00
|
|
|
|> Result.mapError (\_ -> Exception.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
|
|
|
|
)
|
|
|
|
|> DataSource.fromResult
|
|
|
|
)
|
|
|
|
|> DataSource.map
|
|
|
|
(\bool ->
|
|
|
|
if bool then
|
|
|
|
Nothing
|
|
|
|
|
|
|
|
else
|
|
|
|
Just ()
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2022-12-29 04:45:49 +03:00
|
|
|
noteTitle : String -> DataSource Throwable String
|
2021-07-30 20:43:14 +03:00
|
|
|
noteTitle filePath =
|
|
|
|
titleFromFrontmatter filePath
|
|
|
|
|> DataSource.andThen
|
|
|
|
(\maybeTitle ->
|
|
|
|
maybeTitle
|
|
|
|
|> Maybe.map DataSource.succeed
|
|
|
|
|> Maybe.withDefault
|
|
|
|
(StaticFile.bodyWithoutFrontmatter filePath
|
2022-12-29 04:45:49 +03:00
|
|
|
|> DataSource.throw
|
2021-07-30 20:43:14 +03:00
|
|
|
|> DataSource.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)
|
|
|
|
|> DataSource.fromResult
|
2022-12-29 04:45:49 +03:00
|
|
|
|> DataSource.throw
|
2021-07-30 20:43:14 +03:00
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2022-12-29 04:45:49 +03:00
|
|
|
titleAndDescription : String -> DataSource Throwable { title : String, description : String }
|
2021-07-30 20:43:14 +03:00
|
|
|
titleAndDescription filePath =
|
|
|
|
filePath
|
|
|
|
|> StaticFile.onlyFrontmatter
|
2022-01-26 20:47:18 +03:00
|
|
|
(Decode.map2 (\title description -> { title = title, description = description })
|
|
|
|
(Json.Decode.Extra.optionalField "title" Decode.string)
|
|
|
|
(Json.Decode.Extra.optionalField "description" Decode.string)
|
2021-07-30 20:43:14 +03:00
|
|
|
)
|
2022-12-29 04:45:49 +03:00
|
|
|
|> DataSource.throw
|
2021-07-30 20:43:14 +03:00
|
|
|
|> DataSource.andThen
|
|
|
|
(\metadata ->
|
|
|
|
Maybe.map2 (\title description -> { title = title, description = description })
|
|
|
|
metadata.title
|
|
|
|
metadata.description
|
|
|
|
|> Maybe.map DataSource.succeed
|
|
|
|
|> Maybe.withDefault
|
|
|
|
(StaticFile.bodyWithoutFrontmatter filePath
|
2022-12-29 04:45:49 +03:00
|
|
|
|> DataSource.throw
|
2021-07-30 20:43:14 +03:00
|
|
|
|> DataSource.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)
|
2022-12-29 04:45:49 +03:00
|
|
|
|> Result.mapError Exception.fromString
|
2021-07-30 20:43:14 +03:00
|
|
|
|> DataSource.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
|
|
|
)
|
2021-07-30 20:43:14 +03:00
|
|
|
blocks
|
|
|
|
|
|
|
|
|
|
|
|
findDescription : List Block -> String
|
|
|
|
findDescription blocks =
|
|
|
|
blocks
|
|
|
|
|> List.Extra.findMap
|
|
|
|
(\block ->
|
|
|
|
case block of
|
|
|
|
Block.Paragraph inlines ->
|
2021-10-26 20:24:41 +03:00
|
|
|
Just (MarkdownExtra.extractInlineText inlines)
|
2021-07-30 20:43:14 +03:00
|
|
|
|
|
|
|
_ ->
|
|
|
|
Nothing
|
|
|
|
)
|
|
|
|
|> Maybe.withDefault ""
|
|
|
|
|
|
|
|
|
2022-12-29 04:45:49 +03:00
|
|
|
titleFromFrontmatter : String -> DataSource Throwable (Maybe String)
|
2021-07-30 20:43:14 +03:00
|
|
|
titleFromFrontmatter filePath =
|
|
|
|
StaticFile.onlyFrontmatter
|
2022-01-26 20:47:18 +03:00
|
|
|
(Json.Decode.Extra.optionalField "title" Decode.string)
|
2021-07-30 20:43:14 +03:00
|
|
|
filePath
|
2022-12-29 04:45:49 +03:00
|
|
|
|> DataSource.throw
|
2021-06-23 08:14:48 +03:00
|
|
|
|
|
|
|
|
|
|
|
withoutFrontmatter :
|
2021-07-30 20:43:14 +03:00
|
|
|
Markdown.Renderer.Renderer view
|
|
|
|
-> String
|
2022-12-29 04:45:49 +03:00
|
|
|
-> DataSource Throwable (List Block)
|
2021-07-30 20:43:14 +03:00
|
|
|
withoutFrontmatter renderer filePath =
|
2021-08-11 23:03:52 +03:00
|
|
|
(filePath
|
|
|
|
|> StaticFile.bodyWithoutFrontmatter
|
2022-12-29 04:45:49 +03:00
|
|
|
|> DataSource.throw
|
2021-07-30 20:43:14 +03:00
|
|
|
|> DataSource.andThen
|
|
|
|
(\rawBody ->
|
|
|
|
rawBody
|
|
|
|
|> Markdown.Parser.parse
|
2022-12-29 04:45:49 +03:00
|
|
|
|> Result.mapError (\_ -> Exception.fromString "Couldn't parse markdown.")
|
2021-07-30 20:43:14 +03:00
|
|
|
|> DataSource.fromResult
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|> DataSource.andThen
|
|
|
|
(\blocks ->
|
|
|
|
blocks
|
|
|
|
|> Markdown.Renderer.render renderer
|
2022-01-26 21:57:19 +03:00
|
|
|
-- 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)
|
2021-07-30 20:43:14 +03:00
|
|
|
|> DataSource.fromResult
|
2022-12-29 04:45:49 +03:00
|
|
|
|> DataSource.throw
|
2021-07-30 20:43:14 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
withFrontmatter :
|
2022-01-26 21:57:19 +03:00
|
|
|
(frontmatter -> List Block -> value)
|
2021-06-23 08:14:48 +03:00
|
|
|
-> Decoder frontmatter
|
|
|
|
-> Markdown.Renderer.Renderer view
|
|
|
|
-> String
|
2022-12-29 04:45:49 +03:00
|
|
|
-> DataSource Throwable value
|
2022-01-26 21:57:19 +03:00
|
|
|
withFrontmatter constructor frontmatterDecoder_ renderer filePath =
|
2021-06-20 18:18:56 +03:00
|
|
|
DataSource.map2 constructor
|
|
|
|
(StaticFile.onlyFrontmatter
|
2022-01-26 21:57:19 +03:00
|
|
|
frontmatterDecoder_
|
2021-06-20 18:18:56 +03:00
|
|
|
filePath
|
2022-12-29 04:45:49 +03:00
|
|
|
|> DataSource.throw
|
2021-06-20 18:18:56 +03:00
|
|
|
)
|
2022-01-26 21:57:19 +03:00
|
|
|
(StaticFile.bodyWithoutFrontmatter
|
2021-06-20 18:18:56 +03:00
|
|
|
filePath
|
2022-12-29 04:45:49 +03:00
|
|
|
|> DataSource.throw
|
2021-06-20 18:18:56 +03:00
|
|
|
|> DataSource.andThen
|
|
|
|
(\rawBody ->
|
|
|
|
rawBody
|
|
|
|
|> Markdown.Parser.parse
|
2022-12-29 04:45:49 +03:00
|
|
|
|> Result.mapError (\_ -> Exception.fromString "Couldn't parse markdown.")
|
2021-06-20 18:18:56 +03:00
|
|
|
|> DataSource.fromResult
|
|
|
|
)
|
|
|
|
|> DataSource.andThen
|
|
|
|
(\blocks ->
|
|
|
|
blocks
|
|
|
|
|> Markdown.Renderer.render renderer
|
2022-01-26 21:57:19 +03:00
|
|
|
-- 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)
|
2021-06-20 18:18:56 +03:00
|
|
|
|> DataSource.fromResult
|
2022-12-29 04:45:49 +03:00
|
|
|
|> DataSource.throw
|
2021-06-20 18:18:56 +03:00
|
|
|
)
|
|
|
|
)
|