diff --git a/examples/docs/content/docs/02-getting-started.md b/examples/docs/content/docs/02-getting-started.md index c992d3ca..3e4d910c 100644 --- a/examples/docs/content/docs/02-getting-started.md +++ b/examples/docs/content/docs/02-getting-started.md @@ -1,7 +1,3 @@ ---- -description: TODO ---- - # Getting Started You can create a fresh `elm-pages` project with the `init` command. diff --git a/examples/docs/content/docs/08-ports-and-flags.md b/examples/docs/content/docs/08-ports-and-flags.md index 9ac66134..686c1dcb 100644 --- a/examples/docs/content/docs/08-ports-and-flags.md +++ b/examples/docs/content/docs/08-ports-and-flags.md @@ -1,7 +1,3 @@ ---- -description: TODO ---- - # Ports and Flags You can handle ports and flags similar to how you would in a regular Elm application. diff --git a/examples/docs/src/Page/Docs/Section__.elm b/examples/docs/src/Page/Docs/Section__.elm index ed1b1d38..0bd541d5 100644 --- a/examples/docs/src/Page/Docs/Section__.elm +++ b/examples/docs/src/Page/Docs/Section__.elm @@ -70,7 +70,7 @@ routes = data : RouteParams -> DataSource Data data routeParams = - DataSource.map3 Data + DataSource.map4 Data (pageBody routeParams) (previousAndNextData routeParams) (routeParams.section @@ -79,6 +79,7 @@ data routeParams = |> Glob.expectUniqueMatch |> DataSource.map filePathToEditUrl ) + (routeParams |> filePathDataSource |> DataSource.andThen MarkdownCodec.titleAndDescription) filePathToEditUrl : String -> String @@ -175,7 +176,7 @@ head static = , dimensions = Nothing , mimeType = Nothing } - , description = static.data.body.description + , description = static.data.metadata.description , locale = Nothing , title = static.data.titles.title ++ " | elm-pages docs" } @@ -183,9 +184,10 @@ head static = type alias Data = - { body : { description : String, body : List (Html Msg) } + { body : List (Html Msg) , titles : { title : String, previousAndNext : ( Maybe NextPrevious.Item, Maybe NextPrevious.Item ) } , editUrl : String + , metadata : { title : String, description : String } } @@ -239,7 +241,7 @@ view maybeUrl sharedModel static = , Bp.xl [ Tw.pr_36 ] ] ] - (static.data.body.body + (static.data.body ++ [ NextPrevious.view static.data.titles.previousAndNext , Html.hr [] [] , Html.footer @@ -272,8 +274,8 @@ view maybeUrl sharedModel static = } -pageBody : RouteParams -> DataSource { description : String, body : List (Html msg) } -pageBody routeParams = +filePathDataSource : RouteParams -> DataSource String +filePathDataSource routeParams = let slug : String slug = @@ -281,11 +283,14 @@ pageBody routeParams = |> Maybe.withDefault "what-is-elm-pages" in Glob.expectUniqueMatch (findBySlug slug) + + +pageBody : RouteParams -> DataSource (List (Html msg)) +pageBody routeParams = + routeParams + |> filePathDataSource |> DataSource.andThen - (MarkdownCodec.withFrontmatter (\description body -> { description = description, body = body }) - (Decode.field "description" Decode.string) - TailwindMarkdownRenderer.renderer - ) + (MarkdownCodec.withoutFrontmatter TailwindMarkdownRenderer.renderer) findBySlug : String -> Glob String diff --git a/examples/docs/src/MarkdownCodec.elm b/plugins/MarkdownCodec.elm similarity index 58% rename from examples/docs/src/MarkdownCodec.elm rename to plugins/MarkdownCodec.elm index 6bc7db6b..6d6b1bad 100644 --- a/examples/docs/src/MarkdownCodec.elm +++ b/plugins/MarkdownCodec.elm @@ -1,7 +1,8 @@ -module MarkdownCodec exposing (codec, withFrontmatter) +module MarkdownCodec exposing (noteTitle, titleAndDescription, withFrontmatter, withoutFrontmatter) -import DataSource +import DataSource exposing (DataSource) import DataSource.File as StaticFile +import List.Extra import Markdown.Block as Block exposing (Block) import Markdown.Parser import Markdown.Renderer @@ -9,12 +10,156 @@ import OptimizedDecoder exposing (Decoder) import Serialize as S +noteTitle : String -> DataSource String +noteTitle filePath = + titleFromFrontmatter filePath + |> DataSource.andThen + (\maybeTitle -> + maybeTitle + |> Maybe.map DataSource.succeed + |> Maybe.withDefault + (StaticFile.bodyWithoutFrontmatter filePath + |> 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 + ) + ) + ) + |> DataSource.distillSerializeCodec ("note-title-" ++ filePath) S.string + + +titleAndDescription : String -> DataSource { title : String, description : String } +titleAndDescription filePath = + filePath + |> StaticFile.onlyFrontmatter + (OptimizedDecoder.map2 (\title description -> { title = title, description = description }) + (OptimizedDecoder.optionalField "title" OptimizedDecoder.string) + (OptimizedDecoder.optionalField "description" OptimizedDecoder.string) + ) + |> DataSource.andThen + (\metadata -> + Maybe.map2 (\title description -> { title = title, description = description }) + metadata.title + metadata.description + |> Maybe.map DataSource.succeed + |> Maybe.withDefault + (StaticFile.bodyWithoutFrontmatter filePath + |> 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) + |> 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 + ) + blocks + + +findDescription : List Block -> String +findDescription blocks = + blocks + |> List.Extra.findMap + (\block -> + case block of + Block.Paragraph inlines -> + Just (Block.extractInlineText inlines) + + _ -> + Nothing + ) + |> Maybe.withDefault "" + + +titleFromFrontmatter : String -> DataSource (Maybe String) +titleFromFrontmatter filePath = + StaticFile.onlyFrontmatter + (OptimizedDecoder.optionalField "title" OptimizedDecoder.string) + filePath + + +withoutFrontmatter : + Markdown.Renderer.Renderer view + -> String + -> DataSource (List view) +withoutFrontmatter renderer filePath = + (StaticFile.bodyWithoutFrontmatter + filePath + |> DataSource.andThen + (\rawBody -> + rawBody + |> Markdown.Parser.parse + |> Result.mapError (\_ -> "Couldn't parse markdown.") + |> DataSource.fromResult + ) + ) + |> DataSource.distillSerializeCodec ("markdown-blocks-" ++ filePath) + (S.list codec) + |> DataSource.andThen + (\blocks -> + blocks + |> Markdown.Renderer.render renderer + |> DataSource.fromResult + ) + + withFrontmatter : (frontmatter -> List view -> value) -> Decoder frontmatter -> Markdown.Renderer.Renderer view -> String - -> DataSource.DataSource value + -> DataSource value withFrontmatter constructor frontmatterDecoder renderer filePath = DataSource.map2 constructor (StaticFile.onlyFrontmatter @@ -42,39 +187,6 @@ withFrontmatter constructor frontmatterDecoder renderer filePath = ) -withoutFrontmatter : - (frontmatter -> List view -> value) - -> Decoder frontmatter - -> Markdown.Renderer.Renderer view - -> String - -> DataSource.DataSource value -withoutFrontmatter constructor frontmatterDecoder renderer filePath = - DataSource.map2 constructor - (StaticFile.onlyFrontmatter - frontmatterDecoder - filePath - ) - ((StaticFile.bodyWithoutFrontmatter - filePath - |> DataSource.andThen - (\rawBody -> - rawBody - |> Markdown.Parser.parse - |> Result.mapError (\_ -> "Couldn't parse markdown.") - |> DataSource.fromResult - ) - ) - |> DataSource.distillSerializeCodec ("markdown-blocks-" ++ filePath) - (S.list codec) - |> DataSource.andThen - (\blocks -> - blocks - |> Markdown.Renderer.render renderer - |> DataSource.fromResult - ) - ) - - codec : S.Codec Never Block codec = S.customType