Skip to content

Latest commit

 

History

History
261 lines (188 loc) · 7.23 KB

README.md

File metadata and controls

261 lines (188 loc) · 7.23 KB

Fornax.Seo

Nuget version NuGet workflow status Test workflow status

A SEO meta tag generator for Fornax

Goals

  • enhance the search engine visibility of Fornax-generated websites with:

  • try to enforce some SEO best practises, e.g. requiring absolute URLs to all content items

Usage example

  • Install Fornax

    dotnet tool install fornax -g
    
  • Change into a project directory and scaffold a new website

    fornax new
    

IMPORTANT

  • Provide the root domain of your website:
// loaders/globalloader.fsx
#r "../_lib/Fornax.Core.dll"

type SiteInfo = {
    title: string
    /// The root domain of your website - must be an absolute URL
    baseUrl: string
    description: string
}
  • Add personal authorship details, e.g.:
// loaders/globalloader.fsx
#r "nuget: Fornax.Seo"

open Fornax.Seo

let loader (projectRoot: string) (siteContent: SiteContents) =
    let siteInfo =
        { title = "Sample Fornax blog"
          baseUrl = "http://example.com"
          description = "Just a simple blog" }

    let onTheWeb =
        [ "linkedin.com/in/username"
          "github.com/username"
          "bitbucket.org/username"
          "facebook.com/username" ]

    let siteAuthor = { Name = "Moi-même"; Email = "info@example.com"; SocialMedia = onTheWeb }

    siteContent.Add(siteInfo)
    siteContent.Add(siteAuthor)

    siteContent

Collect metadata from a content item (e.g., a blog posting)

// generators/post.fsx
#r "../_lib/Fornax.Core.dll"
#r "nuget: Fornax.Seo"
#load "layout.fsx"

open Html
open Fornax.Seo

let generate' (ctx: SiteContents) (page: string) =
    let siteInfo = ctx.TryGetValue<Globalloader.SiteInfo>()
    let siteName = siteInfo |> Option.map (fun si -> si.title)
    let tagline = siteInfo |> Option.map (fun si -> si.description) |> Option.defaultValue ""
    let siteAuthor = ctx.TryGetValue<ContentCreator>() |> Option.defaultValue ContentCreator.Default

    let siteRoot =
        siteInfo
        |> Option.map (fun si -> si.baseUrl)
        |> Option.defaultValue ContentObject.Default.BaseUrl

    let post =
        ctx.TryGetValues<Postloader.Post>()
        |> Option.defaultValue Seq.empty
        |> Seq.find (fun p -> p.file = page)

    let postMeta =
        { Title = post.title
          BaseUrl = siteRoot
          Url = post.file.Replace(System.IO.Path.GetExtension post.file, ".html")
          Description = tagline
          Author = { siteAuthor with Name = defaultArg post.author siteAuthor.Name }
          SiteName = siteName
          Headline = Some post.summary
          ObjectType = Some "Blog"
          ContentType = Some "BlogPosting"
          OpenGraphType = Some "article"
          Locale = Some "en-us"
          Published = post.published
          Modified = post.modified
          Tags = Some post.tags
          Meta =
              Some [ ("Image", defaultArg post.image $"{siteRoot}/images/avatar.jpg")
                     ("Publisher", defaultArg siteName siteAuthor.Name) ] }

    ctx.Add(postMeta)
    // . . .

Render SEO metadata in your page layout

// generators/layout.fsx
#r "../_lib/Fornax.Core.dll"
#r "nuget: Fornax.Seo"

open Html
open Fornax.Seo

// . . .

let layout (ctx: SiteContents) (active: string) (content: HtmlElement seq) =
    let siteInfo = ctx.TryGetValue<Globalloader.SiteInfo>()
    let siteAuthor = ctx.TryGetValue<ContentCreator>() |> Option.defaultValue ContentCreator.Default
    let pageTitle = siteInfo |> Option.map (fun si -> si.title) |> Option.defaultValue ""
    let tagline = siteInfo |> Option.map (fun si -> si.description) |> Option.defaultValue ""

    let siteRoot =
        siteInfo
        |> Option.map (fun si -> si.baseUrl)
        |> Option.defaultValue ContentObject.Default.BaseUrl

    let seoData =
        ctx.TryGetValues<ContentObject>()
        |> Option.defaultValue Seq.empty

    let pageMeta =
        seoData
        |> Seq.tryFind (fun p -> p.Title.Contains(active))
        |> function
        | Some info -> info
        | _ ->
            { ContentObject.Default with
                  Title = pageTitle
                  Description = tagline
                  BaseUrl = siteRoot
                  SiteName = Some pageTitle
                  Headline = Some tagline
                  Author = siteAuthor }

    html [] [
        head [] [
            meta [ CharSet "utf-8" ]
            meta [ Name "viewport"; Content "width=device-width, initial-scale=1" ]
            title [] [ !!pageTitle ]
            // . . .
            yield! seo pageMeta
        ]
        body [] [
            // . . .
            footer [] [ yield! socialMedia siteAuthor ]
        ]
    ]

    // . . .

Development

All platforms need a .NET SDK at version 5.0.200 or later

Linux users should consider 5.0.202 the safest minimum version

Checkout the source tree and submodules:

git clone --recursive https://github.com/rdipardo/Fornax.Seo

Windows

To run unit tests and build a sample website:

  scripts\ci
  :: or, to also serve the site at localhost:8080
  scripts\ci live

To browse a local copy of the documentation:

  scripts\gendocs live

Linux, macOS

Run tests with:

  scripts/ci
  # or
  scripts/ci live

Browse docs with:

  scripts/gendocs live

Similar NuGet libraries (by framework)

.NET

ASP.NET

License

Distributed under the terms of the Mozilla Public License Version 2.0.