summary refs log tree commit diff stats
path: root/internal/builder
diff options
context:
space:
mode:
Diffstat (limited to 'internal/builder')
-rw-r--r--internal/builder/builder.go205
-rw-r--r--internal/builder/hasher.go13
-rw-r--r--internal/builder/template.go166
3 files changed, 0 insertions, 384 deletions
diff --git a/internal/builder/builder.go b/internal/builder/builder.go
deleted file mode 100644
index df66f87..0000000
--- a/internal/builder/builder.go
+++ /dev/null
@@ -1,205 +0,0 @@
-package builder
-
-import (
-	"fmt"
-	"path"
-	"path/filepath"
-	"slices"
-	"time"
-
-	"go.alanpearce.eu/website/internal/config"
-	"go.alanpearce.eu/website/internal/content"
-	"go.alanpearce.eu/website/internal/sitemap"
-	"go.alanpearce.eu/website/internal/storage/files"
-	"go.alanpearce.eu/website/templates"
-	"go.alanpearce.eu/x/log"
-
-	mapset "github.com/deckarep/golang-set/v2"
-	"gitlab.com/tozd/go/errors"
-)
-
-type IOConfig struct {
-	Source      string `conf:"default:.,short:s,flag:src"`
-	Destination string `conf:"default:public,short:d,flag:dest"`
-	Development bool   `conf:"default:false,flag:dev"`
-}
-
-type Result struct {
-	Hashes []string
-}
-
-func joinSourcePath(src string) func(string) string {
-	return func(rel string) string {
-		return filepath.Join(src, rel)
-	}
-}
-
-func build(ioConfig *IOConfig, config *config.Config, log *log.Logger) (*Result, error) {
-	storage := files.NewWriter(ioConfig.Destination, log, &files.Options{
-		Compress: !ioConfig.Development,
-	})
-
-	joinSource := joinSourcePath(ioConfig.Source)
-
-	log.Debug("output", "dir", ioConfig.Destination)
-	r := &Result{
-		Hashes: make([]string, 0),
-	}
-
-	err := storage.CopyRecursive(joinSource("static"))
-	if err != nil {
-		return nil, errors.WithMessage(err, "could not copy static files")
-	}
-
-	log.Debug("reading posts")
-	posts, tags, err := content.ReadPosts(&content.Config{
-		Root:     joinSource("content"),
-		InputDir: "post",
-	}, log.Named("content"))
-	if err != nil {
-		return nil, err
-	}
-
-	sitemap := sitemap.New(config)
-	lastMod := time.Now()
-	if len(posts) > 0 {
-		lastMod = posts[0].Date
-	}
-
-	for _, post := range posts {
-		log.Debug("rendering post", "post", post.Basename)
-		sitemap.AddPath(post.URL, post.Date)
-		if err := storage.RenderToFile(templates.PostPage(config, post), post.Output); err != nil {
-			return nil, err
-		}
-	}
-
-	log.Debug("rendering tags list")
-	if err := storage.RenderToFile(
-		templates.TagsPage(config, "tags", mapset.Sorted(tags), "/tags"),
-		"tags/",
-	); err != nil {
-		return nil, err
-	}
-	sitemap.AddPath("/tags/", lastMod)
-
-	for _, tag := range tags.ToSlice() {
-		matchingPosts := []content.Post{}
-		for _, post := range posts {
-			if slices.Contains(post.Taxonomies.Tags, tag) {
-				matchingPosts = append(matchingPosts, post)
-			}
-		}
-		log.Debug("rendering tags page", "tag", tag)
-		url := "/tags/" + tag
-		if err := storage.RenderToFile(
-			templates.TagPage(config, tag, matchingPosts, url),
-			path.Join("tags", tag)+"/",
-		); err != nil {
-			return nil, err
-		}
-		sitemap.AddPath(url, matchingPosts[0].Date)
-
-		log.Debug("rendering tags feed", "tag", tag)
-		feed, err := renderFeed(
-			fmt.Sprintf("%s - %s", config.Title, tag),
-			config,
-			matchingPosts,
-			tag,
-		)
-		if err != nil {
-			return nil, errors.WithMessage(err, "could not render tag feed page")
-		}
-		if err := storage.WriterToFile(feed, path.Join("tags", tag, "atom.xml")); err != nil {
-			return nil, err
-		}
-	}
-
-	log.Debug("rendering list page")
-	if err := storage.RenderToFile(
-		templates.ListPage(config, posts, "/post"),
-		"post/",
-	); err != nil {
-		return nil, err
-	}
-	sitemap.AddPath("/post/", lastMod)
-
-	log.Debug("rendering feed")
-	feed, err := renderFeed(config.Title, config, posts, "feed")
-	if err != nil {
-		return nil, errors.WithMessage(err, "could not render feed")
-	}
-	if err := storage.WriterToFile(feed, "atom.xml"); err != nil {
-		return nil, err
-	}
-
-	log.Debug("rendering feed styles")
-	feedStyles, err := renderFeedStyles(ioConfig.Source)
-	if err != nil {
-		return nil, errors.WithMessage(err, "could not render feed styles")
-	}
-	if err := storage.OutputToFile(feedStyles, "feed-styles.xsl"); err != nil {
-		return nil, err
-	}
-	_, err = feedStyles.Seek(0, 0)
-	if err != nil {
-		return nil, err
-	}
-	h, err := getFeedStylesHash(feedStyles)
-	if err != nil {
-		return nil, err
-	}
-	r.Hashes = append(r.Hashes, h)
-
-	log.Debug("rendering homepage")
-	_, text, err := content.GetPost(joinSource(filepath.Join("content", "index.md")))
-	if err != nil {
-		return nil, err
-	}
-	content, err := content.RenderMarkdown(text)
-	if err != nil {
-		return nil, err
-	}
-	if err := storage.RenderToFile(templates.Homepage(config, posts, content), "/"); err != nil {
-		return nil, err
-	}
-	// it would be nice to set LastMod here, but using the latest post
-	// date would be wrong as the homepage has its own content file
-	// without a date, which could be newer
-	sitemap.AddPath("/", time.Time{})
-	f, err := storage.Open("/")
-	if err != nil {
-		return nil, err
-	}
-	defer f.Close()
-	h, _ = getHTMLStyleHash(f)
-	r.Hashes = append(r.Hashes, h)
-
-	log.Debug("rendering sitemap")
-	if err := storage.WriterToFile(sitemap, "sitemap.xml"); err != nil {
-		return nil, err
-	}
-
-	log.Debug("rendering robots.txt")
-	rob, err := renderRobotsTXT(ioConfig.Source, config)
-	if err != nil {
-		return nil, err
-	}
-	if err := storage.OutputToFile(rob, "robots.txt"); err != nil {
-		return nil, err
-	}
-
-	return r, nil
-}
-
-func BuildSite(ioConfig *IOConfig, cfg *config.Config, log *log.Logger) (*Result, error) {
-	if cfg == nil {
-		return nil, errors.New("config is nil")
-	}
-	cfg.InjectLiveReload = ioConfig.Development
-
-	templates.Setup()
-	loadCSS(ioConfig.Source)
-
-	return build(ioConfig, cfg, log)
-}
diff --git a/internal/builder/hasher.go b/internal/builder/hasher.go
deleted file mode 100644
index f0f9167..0000000
--- a/internal/builder/hasher.go
+++ /dev/null
@@ -1,13 +0,0 @@
-package builder
-
-import (
-	"crypto/sha256"
-	"encoding/base64"
-)
-
-func hash(s string) string {
-	shasum := sha256.New()
-	shasum.Write([]byte(s))
-
-	return "sha256-" + base64.StdEncoding.EncodeToString(shasum.Sum(nil))
-}
diff --git a/internal/builder/template.go b/internal/builder/template.go
deleted file mode 100644
index 914b67d..0000000
--- a/internal/builder/template.go
+++ /dev/null
@@ -1,166 +0,0 @@
-package builder
-
-import (
-	"bytes"
-	"encoding/xml"
-	"io"
-	"os"
-	"path/filepath"
-	"strings"
-	"text/template"
-
-	"go.alanpearce.eu/website/internal/atom"
-	"go.alanpearce.eu/website/internal/config"
-	"go.alanpearce.eu/website/internal/content"
-
-	"github.com/PuerkitoBio/goquery"
-	"github.com/antchfx/xmlquery"
-	"github.com/antchfx/xpath"
-	"gitlab.com/tozd/go/errors"
-)
-
-var (
-	css   string
-	nsMap = map[string]string{
-		"xsl":   "http://www.w3.org/1999/XSL/Transform",
-		"atom":  "http://www.w3.org/2005/Atom",
-		"xhtml": "http://www.w3.org/1999/xhtml",
-	}
-)
-
-func loadCSS(source string) {
-	bytes, err := os.ReadFile(filepath.Join(source, "templates/style.css"))
-	if err != nil {
-		panic(err)
-	}
-	css = string(bytes)
-}
-
-type QuerySelection struct {
-	*goquery.Selection
-}
-
-type QueryDocument struct {
-	*goquery.Document
-}
-
-func NewDocumentFromReader(r io.Reader) (*QueryDocument, error) {
-	doc, err := goquery.NewDocumentFromReader(r)
-
-	return &QueryDocument{doc}, errors.WithMessage(err, "could not create query document")
-}
-
-func (q *QueryDocument) Find(selector string) *QuerySelection {
-	return &QuerySelection{q.Document.Find(selector)}
-}
-
-func renderRobotsTXT(source string, config *config.Config) (io.Reader, error) {
-	r, w := io.Pipe()
-	tpl, err := template.ParseFiles(filepath.Join(source, "templates/robots.tmpl"))
-	if err != nil {
-		return nil, err
-	}
-	go func() {
-		err = tpl.Execute(w, map[string]interface{}{
-			"BaseURL": config.BaseURL,
-		})
-		if err != nil {
-			w.CloseWithError(err)
-		}
-		w.Close()
-	}()
-
-	return r, nil
-}
-
-func renderFeed(
-	title string,
-	config *config.Config,
-	posts []content.Post,
-	specific string,
-) (io.WriterTo, error) {
-	buf := &bytes.Buffer{}
-	datetime := posts[0].Date.UTC()
-
-	buf.WriteString(xml.Header)
-	err := atom.LinkXSL(buf, "/feed-styles.xsl")
-	if err != nil {
-		return nil, err
-	}
-	feed := &atom.Feed{
-		Title:   title,
-		Link:    atom.MakeLink(config.BaseURL.URL),
-		ID:      atom.MakeTagURI(config, specific),
-		Updated: datetime,
-		Entries: make([]*atom.FeedEntry, len(posts)),
-	}
-
-	for i, post := range posts {
-		feed.Entries[i] = &atom.FeedEntry{
-			Title:   post.Title,
-			Link:    atom.MakeLink(config.BaseURL.JoinPath(post.URL)),
-			ID:      atom.MakeTagURI(config, post.Basename),
-			Updated: post.Date.UTC(),
-			Summary: post.Description,
-			Author:  config.Title,
-			Content: atom.FeedContent{
-				Content: post.Content,
-				Type:    "html",
-			},
-		}
-	}
-	enc := xml.NewEncoder(buf)
-	err = enc.Encode(feed)
-	if err != nil {
-		return nil, err
-	}
-
-	return buf, nil
-}
-
-func renderFeedStyles(source string) (*strings.Reader, error) {
-	tpl, err := template.ParseFiles(filepath.Join(source, "templates/feed-styles.xsl"))
-	if err != nil {
-		return nil, err
-	}
-
-	esc := &strings.Builder{}
-	err = xml.EscapeText(esc, []byte(css))
-	if err != nil {
-		return nil, err
-	}
-
-	w := &strings.Builder{}
-	err = tpl.Execute(w, map[string]interface{}{
-		"css": esc.String(),
-	})
-	if err != nil {
-		return nil, err
-	}
-
-	return strings.NewReader(w.String()), nil
-}
-
-func getFeedStylesHash(r io.Reader) (string, error) {
-	doc, err := xmlquery.Parse(r)
-	if err != nil {
-		return "", err
-	}
-	expr, err := xpath.CompileWithNS("//xhtml:style", nsMap)
-	if err != nil {
-		return "", errors.WithMessage(err, "could not parse XPath")
-	}
-	style := xmlquery.QuerySelector(doc, expr)
-
-	return hash(style.InnerText()), nil
-}
-
-func getHTMLStyleHash(r io.Reader) (string, error) {
-	doc, err := NewDocumentFromReader(r)
-	if err != nil {
-		return "", err
-	}
-	html := doc.Find("head > style").Text()
-
-	return hash(html), nil
-}