all repos — homestead @ 02344e6cb41515516464de403e2eae1caac81e5c

Code for my website

switch from templ to gomponent

Alan Pearce
commit

02344e6cb41515516464de403e2eae1caac81e5c

parent

4034ac2a849b499364d82b902896ca899d946c3a

M .golangci.yaml.golangci.yaml
@@ -19,3 +19,9 @@ - reassign
- revive - sloglint - unconvert +issues: + exclude-rules: + - linters: + - revive + path: templates/ + text: "dot-imports"
M cmd/build/default.nixcmd/build/default.nix
@@ -10,7 +10,6 @@ ];
} ) , buildGoApplication ? pkgs.buildGoApplication -, templ ? pkgs.templ , makeWrapper ? pkgs.makeWrapper }:
@@ -20,9 +19,6 @@ version = "0.1";
pwd = ../..; src = ../..; modules = ../../gomod2nix.toml; - patchPhase = '' - ${templ}/bin/templ generate - ''; subPackages = [ "cmd/build" ]; nativeBuildInputs = [ makeWrapper ];
M flake.nixflake.nix
@@ -23,7 +23,6 @@ ];
}; commonShellPackages = with pkgs; [ go - templ hyperlink just ko
M go.modgo.mod
@@ -7,7 +7,6 @@
require ( github.com/BurntSushi/toml v1.4.0 github.com/PuerkitoBio/goquery v1.10.2 - github.com/a-h/templ v0.3.833 github.com/adrg/frontmatter v0.2.0 github.com/andybalholm/brotli v1.1.1 github.com/antchfx/xmlquery v1.4.4
@@ -30,6 +29,7 @@ github.com/yuin/goldmark v1.7.8
gitlab.com/tozd/go/errors v0.10.0 go.alanpearce.eu/x v0.0.0-20250213214218-1bdfdc914d6c go.uber.org/zap v1.27.0 + maragu.dev/gomponents v1.1.0 modernc.org/sqlite v1.36.0 )
M go.sumgo.sum
@@ -12,8 +12,6 @@ github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw=
github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= github.com/PuerkitoBio/goquery v1.10.2 h1:7fh2BdHcG6VFZsK7toXBT/Bh1z5Wmy8Q9MV9HqT2AM8= github.com/PuerkitoBio/goquery v1.10.2/go.mod h1:0guWGjcLu9AYC7C1GHnpysHy056u9aEkUHwhdnePMCU= -github.com/a-h/templ v0.3.833 h1:L/KOk/0VvVTBegtE0fp2RJQiBm7/52Zxv5fqlEHiQUU= -github.com/a-h/templ v0.3.833/go.mod h1:cAu4AiZhtJfBjMY0HASlyzvkrtjnHWPeEsyGK2YYmfk= github.com/adrg/frontmatter v0.2.0 h1:/DgnNe82o03riBd1S+ZDjd43wAmC6W35q67NHeLkPd4= github.com/adrg/frontmatter v0.2.0/go.mod h1:93rQCj3z3ZlwyxxpQioRKC1wDLto4aXHrbqIsnH9wmE= github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
@@ -346,6 +344,8 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +maragu.dev/gomponents v1.1.0 h1:iCybZZChHr1eSlvkWp/JP3CrZGzctLudQ/JI3sBcO4U= +maragu.dev/gomponents v1.1.0/go.mod h1:oEDahza2gZoXDoDHhw8jBNgH+3UR5ni7Ur648HORydM= modernc.org/cc/v4 v4.24.4 h1:TFkx1s6dCkQpd6dKurBNmpo+G8Zl4Sq/ztJ+2+DEsh0= modernc.org/cc/v4 v4.24.4/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= modernc.org/ccgo/v4 v4.23.16 h1:Z2N+kk38b7SfySC1ZkpGLN2vthNJP1+ZzGZIlH7uBxo=
M gomod2nix.tomlgomod2nix.toml
@@ -19,9 +19,6 @@ hash = "sha256-XlFT3uxgpPYFTND54uO8fH33jtQqAHWa7zrv24nw/PE="
[mod."github.com/PuerkitoBio/goquery"] version = "v1.10.2" hash = "sha256-2h4Ol31ucXHieeZUJq+xi7F5m2FzzsOKRLjUehA5lbE=" - [mod."github.com/a-h/templ"] - version = "v0.3.833" - hash = "sha256-OSGAJDVPYqgb72DZXeemLp37aGrrxMt+PQNKW5voKQ0=" [mod."github.com/adrg/frontmatter"] version = "v0.2.0" hash = "sha256-WJsVcdCpkIkjqUz5fJOFStZYwQlrcFzQ6+mZatZiimo="
@@ -229,6 +226,9 @@ hash = "sha256-ATVL9yEmgYbkJ1DkltDGRn/auGAjqGOfjQyBYyUo8s8="
[mod."gopkg.in/yaml.v2"] version = "v2.4.0" hash = "sha256-uVEGglIedjOIGZzHW4YwN1VoRSTK8o0eGZqzd+TNdd0=" + [mod."maragu.dev/gomponents"] + version = "v1.1.0" + hash = "sha256-Nu95Iy1gy0NsjSsGjihuC2k8IrMpSXboWtEm9cyyY2g=" [mod."modernc.org/libc"] version = "v1.61.13" hash = "sha256-hXpwqDrTCUDmicfa1CfS62e4K/M6uOlv06xY1kxEYEY="
M internal/builder/builder.gointernal/builder/builder.go
@@ -1,7 +1,6 @@
package builder import ( - "context" "fmt" "io" "os"
@@ -63,7 +62,6 @@ options *Options,
config *config.Config, log *log.Logger, ) errors.E { - ctx := context.TODO() buf := new(buffer.Buffer) joinSource := joinSourcePath(options.Source) storage := options.Storage
@@ -95,7 +93,7 @@ for _, post := range cc.Posts {
log.Debug("rendering post", "post", post.Basename) sitemap.AddPath(post.URL, post.Date) buf.Reset() - if err := templates.PostPage(siteSettings, post).Render(ctx, buf); err != nil { + if err := templates.PostPage(siteSettings, post).Render(buf); err != nil { return errors.WithMessage(err, "could not render post") }
@@ -106,7 +104,10 @@ }
log.Debug("rendering tags list") buf.Reset() - if err := templates.TagsPage(siteSettings, "tags", mapset.Sorted(cc.Tags), "/tags").Render(ctx, buf); err != nil { + if err := templates.TagsPage(siteSettings, templates.TagsPageVars{ + Title: "Tags", + Tags: mapset.Sorted(cc.Tags), + }).Render(buf); err != nil { return errors.WithStack(err) } if err := storage.Write("/tags/", "Tags", buf); err != nil {
@@ -124,7 +125,10 @@ }
log.Debug("rendering tags page", "tag", tag) url := path.Join("/tags", tag) + "/" buf.Reset() - if err := templates.TagPage(siteSettings, tag, matchingPosts, url).Render(ctx, buf); err != nil { + if err := templates.TagPage(siteSettings, templates.TagPageVars{ + Tag: tag, + Posts: matchingPosts, + }).Render(buf); err != nil { return errors.WithStack(err) } if err = storage.Write(url, tag, buf); err != nil {
@@ -154,7 +158,9 @@ }
log.Debug("rendering list page") buf.Reset() - if err := templates.ListPage(siteSettings, cc.Posts, path.Join("/", postDir)).Render(ctx, buf); err != nil { + if err := templates.ListPage(siteSettings, templates.ListPageVars{ + Posts: cc.Posts, + }).Render(buf); err != nil { return errors.WithStack(err) } if err := storage.Write(path.Join("/", postDir)+"/", "Posts", buf); err != nil {
@@ -194,11 +200,11 @@ if err := templates.Homepage(siteSettings, templates.HomepageVars{
Email: config.Email, Me: config.RelMe, Posts: cc.Posts, - }, post).Render(ctx, buf); err != nil { + }, post).Render(buf); err != nil { return errors.WithStack(err) } } else { - if err := templates.Page(siteSettings, post).Render(ctx, buf); err != nil { + if err := templates.Page(siteSettings, post).Render(buf); err != nil { return errors.WithStack(err) } }
@@ -227,6 +233,7 @@ return errors.WithStack(err)
} log.Debug("rendering robots.txt") + buf.Reset() rob, err := template.RenderRobotsTXT(config) if err != nil { return errors.WithStack(err)
M internal/content/posts.gointernal/content/posts.go
@@ -2,7 +2,6 @@ package content
import ( "bytes" - "context" "io" "io/fs" "os"
@@ -140,14 +139,14 @@
return nil } -// implements templ.Component -func (p *Post) Render(_ context.Context, w io.Writer) error { +// implements gomponent.Node +func (p *Post) Render(w io.Writer) error { return markdown.Convert(p.content, w) } func (p *Post) RenderString() (string, errors.E) { var buf bytes.Buffer - if err := p.Render(context.Background(), &buf); err != nil { + if err := p.Render(&buf); err != nil { return "", errors.WithMessage(err, "could not convert markdown content") }
M internal/events/file.gointernal/events/file.go
@@ -17,7 +17,6 @@ )
var ( ignores = []string{ - "*.templ", "*.go", "*-journal", }
M internal/website/mux.gointernal/website/mux.go
@@ -32,7 +32,7 @@
func (website *Website) ErrorHandler(err *ihttp.Error, w http.ResponseWriter, r *http.Request) { if strings.Contains(r.Header.Get("Accept"), "text/html") { w.WriteHeader(err.Code) - err := templates.Error(*website.siteSettings, r.URL.Path, err).Render(r.Context(), w) + err := templates.Error(*website.siteSettings, err).Render(w) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) }
M justfilejustfile
@@ -25,7 +25,6 @@ go get -u all
gomod2nix build *BUILD_FLAGS: - templ generate SOURCE={{ website_repo }} go run ./cmd/build {{ BUILD_FLAGS }} run: dev
@@ -42,5 +41,4 @@ cd *DEPLOY_FLAGS: && (deploy DEPLOY_FLAGS)
fly auth docker deploy *DEPLOY_FLAGS: - templ generate fly deploy --image $(KO_DOCKER_REPO={{ docker_registry }} ko build --platform linux/amd64 --sbom none --bare --tags {{ docker-tag }} .) {{ DEPLOY_FLAGS }}
M modd.confmodd.conf
@@ -1,4 +1,3 @@
-**/*.go !**/*_templ.go { - daemon +sigint: templ generate --watch \ - --cmd="go run . --dev" +**/*.go { + daemon +sigint: go run . --dev }
A templates/error.go
@@ -0,0 +1,19 @@
+package templates + +import ( + "strconv" + + "go.alanpearce.eu/homestead/internal/http" + + g "maragu.dev/gomponents" + . "maragu.dev/gomponents/html" +) + +func Error(site SiteSettings, err *http.Error) g.Node { + return Layout(site, PageSettings{ + Title: "Error", + }, Div( + H1(g.Text(strconv.Itoa(err.Code)+" "+err.Message)), + H2(g.Text("ʕノ•ᴥ•ʔノ ︵ ┻━┻")), + )) +}
D templates/error.templ
@@ -1,16 +0,0 @@
-package templates - -import ( - "go.alanpearce.eu/homestead/internal/http" - "strconv" -) - -templ Error(site SiteSettings, path string, err *http.Error) { - @Layout(site, PageSettings{ - Title: "Error", - Path: path, - }) { - <h1>{ strconv.Itoa(err.Code) } { err.Message }</h1> - <h2>ʕノ•ᴥ•ʔノ ︵ ┻━┻</h2> - } -}
A templates/homepage.go
@@ -0,0 +1,45 @@
+package templates + +import ( + "go.alanpearce.eu/homestead/internal/config" + "go.alanpearce.eu/homestead/internal/content" + + g "maragu.dev/gomponents" + . "maragu.dev/gomponents/html" +) + +type HomepageVars struct { + Email string + Me []config.MenuItem + Posts []*content.Post +} + +func Homepage(site SiteSettings, vars HomepageVars, content g.Node) g.Node { + return Layout(site, PageSettings{ + Title: site.Title, + TitleAttrs: Class("p-name u-url"), + BodyAttrs: Class("h-card"), + }, + Div( + ID("content"), + content, + ), + Section( + H2(g.Text("Latest Posts")), + list(vars.Posts[0:5]), + ), + Section( + H2(g.Text("Elsewhere on the Internet")), + Ul(Class("elsewhere"), + Li( + A(Class("u-email"), Rel("me"), Href("mailto:"+vars.Email), g.Text(vars.Email)), + ), + g.Map(vars.Me, func(link config.MenuItem) g.Node { + return Li( + A(Class("u-url"), Rel("me"), Href(link.URL.String()), g.Text(link.Name)), + ) + }), + ), + ), + ) +}
D templates/homepage.templ
@@ -1,48 +0,0 @@
-package templates - -import ( - "go.alanpearce.eu/homestead/internal/config" - "go.alanpearce.eu/homestead/internal/content" -) - -type HomepageVars struct { - Email string - Me []config.MenuItem - Posts []*content.Post -} - -templ Homepage(site SiteSettings, vars HomepageVars, content templ.Component) { - @Layout(site, PageSettings{ - Title: site.Title, - TitleAttrs: templ.Attributes{ - "class": "p-name u-url", - }, - Path: "/", - BodyAttrs: templ.Attributes{ - "class": "h-card", - }, - }) { - <div id="content"> - @content - </div> - <section> - <h2>Latest Posts</h2> - @list(vars.Posts[0:5]) - </section> - <section> - <h2>Elsewhere on the Internet</h2> - <ul class="elsewhere"> - <li> - <a class="u-email" rel="me" href={ templ.SafeURL("mailto:" + vars.Email) }> - { vars.Email } - </a> - </li> - for _, link := range vars.Me { - <li> - <a class="u-url" rel="me" href={ templ.SafeURL(link.URL.String()) }>{ link.Name }</a> - </li> - } - </ul> - </section> - } -}
A templates/layout.go
@@ -0,0 +1,78 @@
+package templates + +import ( + "go.alanpearce.eu/homestead/internal/config" + + g "maragu.dev/gomponents" + . "maragu.dev/gomponents/html" +) + +type SiteSettings struct { + Title string + Language string + Menu []config.MenuItem + InjectLiveReload bool +} + +type PageSettings struct { + Title string + TitleAttrs g.Node + BodyAttrs g.Node +} + +func Layout(site SiteSettings, page PageSettings, children ...g.Node) g.Node { + return Doctype( + HTML( + Lang(site.Language), + Head( + Meta(Charset("utf-8")), + Meta(Name("viewport"), Content("width=device-width, initial-scale=1.0")), + TitleEl(g.Text(page.Title)), + Link( + Rel("alternate"), + Type("application/atom+xml"), + Title(site.Title), + Href("/atom.xml"), + ), + StyleEl(g.Raw(CSS)), + ), + Body( + A(Class("skip"), Href("#main"), g.Text("Skip to main content")), + Header( + H2( + A(page.TitleAttrs, Class("title p-name"), Href("/"), g.Text(site.Title)), + ), + Nav( + g.Map(site.Menu, func(item config.MenuItem) g.Node { + return A( + Href(item.URL.String()), + g.If(item.URL.IsAbs(), Target("_blank")), + g.Text(item.Name), + ) + }), + ), + ), + Main(ID("main"), g.Group(children)), + Footer( + g.Text("Content is "), + A( + Rel("license"), + Href("http://creativecommons.org/licenses/by/4.0/"), + g.Text("CC BY 4.0"), + ), + g.Text(". "), + A(Href("https://git.alanpearce.eu/website/"), g.Text("Site source code")), + g.Text(" is "), + A(Href("https://opensource.org/licenses/MIT"), g.Text("MIT")), + ), + g.If(site.InjectLiveReload, + Script(Defer(), g.Raw(` + new EventSource("/_/reload").onmessage = event => { + console.log("got message", event) + window.location.reload() + }; + `)), + ), + ), + )) +}
D templates/layout.templ
@@ -1,102 +0,0 @@
-package templates - -import ( - "context" - "go.alanpearce.eu/homestead/internal/config" - "io" - "net/url" -) - -type SiteSettings struct { - Title string - Language string - Menu []config.MenuItem - InjectLiveReload bool -} - -type PageSettings struct { - Title string - Path string - TitleAttrs templ.Attributes - BodyAttrs templ.Attributes -} - -func extendClasses(cs string, attrs templ.Attributes) string { - if extras, exists := attrs["class"]; exists { - return templ.Classes(cs, extras).String() - } else { - return cs - } -} - -templ menuItem(item config.MenuItem) { - <a - href={ templ.SafeURL(item.URL.String()) } - if item.URL.IsAbs() { - target="_blank" - } - >{ item.Name }</a> -} - -templ Layout(site SiteSettings, page PageSettings) { - <!DOCTYPE html> - <html lang={ site.Language }> - <head> - <meta charset="utf-8"/> - <meta name="viewport" content="width=device-width, initial-scale=1.0"/> - <title>{ page.Title }</title> - <link rel="alternate" type="application/atom+xml" title={ site.Title } href="/atom.xml"/> - @style(CSS) - </head> - <body { page.BodyAttrs... }> - <a class="skip" href="#main">Skip to main content</a> - <header> - <h2> - <a href="/" class={ extendClasses("title p-name", page.TitleAttrs) } { page.TitleAttrs... }>{ site.Title }</a> - </h2> - <nav> - for _, item := range site.Menu { - @menuItem(item) - } - </nav> - </header> - <main id="main"> - { children... } - </main> - <footer> - Content is - <a rel="license" href="http://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a>. - <a href="https://git.alanpearce.eu/website/">Site source code</a> is - <a href="https://opensource.org/licenses/MIT">MIT</a> - </footer> - if site.InjectLiveReload { - <script defer> - new EventSource("/_/reload").onmessage = event => { - console.log("got message", event) - window.location.reload() - }; - </script> - } - </body> - </html> -} - -func mkURL(original config.URL, path string, title string) string { - ou := *original.URL - u := config.URL{ - URL: &ou, - } - q := url.Values{} - q.Add("p", path) - q.Add("t", title) - u.RawQuery = q.Encode() - - return u.String() -} - -func style(css string) templ.Component { - return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) { - _, err = io.WriteString(w, "<style>\n"+css+"\n</style>") - return - }) -}
A templates/list.go
@@ -0,0 +1,56 @@
+package templates + +import ( + "go.alanpearce.eu/homestead/internal/content" + + g "maragu.dev/gomponents" + . "maragu.dev/gomponents/html" +) + +type TagPageVars struct { + Tag string + Posts []*content.Post +} + +func TagPage(site SiteSettings, vars TagPageVars) g.Node { + return Layout(site, PageSettings{ + Title: vars.Tag, + TitleAttrs: Class("p-author h-card"), + }, Div( + ID("content"), + Div(Class("filter"), + H3(Class("filter"), g.Text(vars.Tag)), + Small( + A(Href("../"), g.Text("Remove filter")), + ), + ), + list(vars.Posts), + )) +} + +type ListPageVars struct { + Posts []*content.Post +} + +func ListPage(site SiteSettings, vars ListPageVars) g.Node { + return Layout(site, PageSettings{ + Title: site.Title, + TitleAttrs: Class("p-author h-card"), + }, + Div( + ID("content"), + list(vars.Posts), + ), + ) +} + +func list(posts []*content.Post) g.Node { + return Ul(Class("h-feed"), g.Map(posts, func(post *content.Post) g.Node { + return Li(Class("h-entry"), + Span( + postDate(post.Date, "dt-published"), + ), + A(Class("p-name u-url"), Href(post.URL), g.Text(post.Title)), + ) + })) +}
D templates/list.templ
@@ -1,48 +0,0 @@
-package templates - -import "go.alanpearce.eu/homestead/internal/content" - -templ TagPage(site SiteSettings, tag string, posts []*content.Post, path string) { - @Layout(site, PageSettings{ - Title: tag, - Path: path, - TitleAttrs: templ.Attributes{ - "class": "p-author h-card", - "rel": "author", - }, - }) { - <div class="filter"> - <h3 class="filter">#{ tag }</h3> - <small> - <a href="../">Remove filter</a> - </small> - </div> - @list(posts) - } -} - -templ ListPage(site SiteSettings, posts []*content.Post, path string) { - @Layout(site, PageSettings{ - Title: site.Title, - TitleAttrs: templ.Attributes{ - "class": "p-author h-card", - "rel": "author", - }, - Path: path, - }) { - @list(posts) - } -} - -templ list(posts []*content.Post) { - <ul class="h-feed"> - for _, post := range posts { - <li class="h-entry"> - <span> - @postDate(post.Date, "dt-published") - </span> - <a class="p-name u-url" href={ templ.SafeURL(post.URL) }>{ post.Title }</a> - </li> - } - </ul> -}
A templates/page.go
@@ -0,0 +1,36 @@
+package templates + +import ( + "go.alanpearce.eu/homestead/internal/content" + + g "maragu.dev/gomponents" + . "maragu.dev/gomponents/html" +) + +func Page(site SiteSettings, page *content.Post) g.Node { + return Layout(site, PageSettings{ + Title: page.Title, + TitleAttrs: g.Group([]g.Node{Class("h-card"), Rel("author")}), + }, Article( + Header( + H1(Class("p-name"), g.Text(page.Title)), + P( + Class("meta"), + g.If(!page.Date.IsZero(), + Span(Class("date"), + g.Text("Published: "), + A(Class("u-url"), Href(page.URL), postDate(page.Date, "dt-published")), + ), + ), + // one commit: not updated + g.Iff( + (page.Date.IsZero() && len(page.Commits) > 0) || len(page.Commits) > 1, + lastUpdated(page), + ), + ), + ), + Div(Class("content"), + page, + ), + )) +}
D templates/page.templ
@@ -1,42 +0,0 @@
-package templates - -import "go.alanpearce.eu/homestead/internal/content" - -templ Page(site SiteSettings, page *content.Post) { - @Layout(site, PageSettings{ - Title: page.Title, - TitleAttrs: templ.Attributes{ - "class": "h-card", - "rel": "author", - }, - Path: page.URL, - }) { - <article> - <header> - <h1 class="p-name">{ page.Title }</h1> - <p class="meta"> - if !page.Date.IsZero() { - <span class="date"> - Published: - <a class="u-url" href={ templ.SafeURL(page.URL) }> - @postDate(page.Date, "dt-published") - </a> - </span> - } - // one commit: not updated - if (page.Date.IsZero() && len(page.Commits) > 0) || len(page.Commits) > 1 { - <span class="date last-updated"> - Last updated: - <a href={ templ.URL(page.Commits[0].Link.String()) }> - @postDate(page.Commits[0].Date, "dt-updated") - </a> - </span> - } - </p> - </header> - </article> - <div class="content"> - @page - </div> - } -}
A templates/post.go
@@ -0,0 +1,63 @@
+package templates + +import ( + "time" + + "go.alanpearce.eu/homestead/internal/content" + + g "maragu.dev/gomponents" + . "maragu.dev/gomponents/html" +) + +func PostPage(site SiteSettings, post *content.Post) g.Node { + return Layout(site, PageSettings{ + Title: post.Title, + TitleAttrs: g.Group([]g.Node{Class("p-author h-card"), Rel("author")}), + BodyAttrs: Class("h-entry"), + }, Article( + Header( + H1(Class("p-name"), g.Text(post.Title)), + P(Class("meta"), + Span(Class("date"), + g.Text("Published: "), + A(Class("u-url"), Href(post.URL), postDate(post.Date, "dt-published")), + ), + g.Iff(len(post.Commits) > 1, lastUpdated(post)), + ), + ), + Div(Class("e-content"), + post, + ), + Div(Class("tags"), + g.Text("Tags: "), + Ul(Class("p-categories tags"), + g.Map(post.Taxonomies.Tags, func(tag string) g.Node { + return Li( + tagLink(tag, Class("p-category")), + ) + }), + ), + ), + )) +} + +func postDate(d time.Time, class string) g.Node { + return Time( + Class(class), + DateTime(d.UTC().Format(time.RFC3339)), + g.Text(d.Format("2006-01-02")), + ) +} + +func lastUpdated(post *content.Post) func() g.Node { + return func() g.Node { + return Span( + Class("date last-updated"), + g.Text("Last updated: "), + A( + Href(post.Commits[0].Link.String()), + postDate(post.Commits[0].Date, "dt-updated"), + ), + ) + } +}
D templates/post.templ
@@ -1,72 +0,0 @@
-package templates - -import ( - "context" - "io" - "time" - - "go.alanpearce.eu/homestead/internal/content" -) - -func Unsafe(html string) templ.Component { - return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) { - _, err = io.WriteString(w, html) - return - }) -} - -templ postDate(d time.Time, class string) { - <time class={ class } datetime={ d.UTC().Format(time.RFC3339) }> - { d.Format("2006-01-02") } - </time> -} - -templ PostPage(site SiteSettings, post *content.Post) { - @Layout(site, PageSettings{ - Title: post.Title, - TitleAttrs: templ.Attributes{ - "class": "p-author h-card", - "rel": "author", - }, - BodyAttrs: templ.Attributes{ - "class": "h-entry", - }, - Path: post.URL, - }) { - <article> - <header> - <h1 class="p-name">{ post.Title }</h1> - <p class="meta"> - <span class="date"> - Published: - <a class="u-url" href={ templ.SafeURL(post.URL) }> - @postDate(post.Date, "dt-published") - </a> - </span> - // one commit: not updated - if len(post.Commits) > 1 { - <span class="date last-updated"> - Last updated: - <a href={ templ.URL(post.Commits[0].Link.String()) }> - @postDate(post.Commits[0].Date, "dt-updated") - </a> - </span> - } - </p> - </header> - <div class="e-content"> - @post - </div> - <div class="tags"> - Tags: - <ul class="p-categories tags"> - for _, tag := range post.Taxonomies.Tags { - <li> - @tagLink(tag, templ.Attributes{"class": "p-category"}) - </li> - } - </ul> - </div> - </article> - } -}
A templates/tags.go
@@ -0,0 +1,30 @@
+package templates + +import ( + g "maragu.dev/gomponents" + . "maragu.dev/gomponents/html" +) + +type TagsPageVars struct { + Title string + Tags []string +} + +func TagsPage(site SiteSettings, vars TagsPageVars) g.Node { + return Layout(site, PageSettings{ + Title: vars.Title, + }, Div( + H3(Class("filter"), g.Text("Tags")), + Ul(Class("tags"), + g.Map(vars.Tags, func(tag string) g.Node { + return Li(Class("h-feed"), + tagLink(tag, Class("")), + ) + }), + ), + )) +} + +func tagLink(tag string, attrs g.Node) g.Node { + return A(attrs, Href("/tags/"+tag), g.Text("#"+tag)) +}
D templates/tags.templ
@@ -1,21 +0,0 @@
-package templates - -templ tagLink(tag string, attrs templ.Attributes) { - <a { attrs... } href={ templ.SafeURL("/tags/" + tag + "/") }>#{ tag }</a> -} - -templ TagsPage(site SiteSettings, title string, tags []string, path string) { - @Layout(site, PageSettings{ - Title: title, - Path: path, - }) { - <h3 class="filter">Tags</h3> - <ul class="tags"> - for _, tag := range tags { - <li class="h-feed"> - @tagLink(tag, templ.Attributes{}) - </li> - } - </ul> - } -}