summaryrefslogtreecommitdiff
path: root/internal/build/build.go
diff options
context:
space:
mode:
authorHimanshu Sardana <himanshusardana2005@gmail.com>2026-03-25 09:21:19 +0000
committerHimanshu Sardana <himanshusardana2005@gmail.com>2026-03-25 09:21:19 +0000
commitaf0332e7d805f7a4cb1086b66f61ad706a082768 (patch)
tree24622ad88fa1edf636e5c7fac5ac3f5d8e002e6d /internal/build/build.go
parent7cfdc43226930b21bae2e02581672a338a9c0789 (diff)
refactor: split into cmd/internal
Diffstat (limited to 'internal/build/build.go')
-rw-r--r--internal/build/build.go240
1 files changed, 240 insertions, 0 deletions
diff --git a/internal/build/build.go b/internal/build/build.go
new file mode 100644
index 0000000..1fe85b3
--- /dev/null
+++ b/internal/build/build.go
@@ -0,0 +1,240 @@
+package build
+
+import (
+ "fmt"
+ "html/template"
+ "io/fs"
+ "log"
+ "os"
+ "path/filepath"
+ "sort"
+ "strings"
+ "time"
+
+ "gopkg.in/yaml.v2"
+
+ "github.com/adrg/frontmatter"
+ "github.com/gomarkdown/markdown"
+ "github.com/gomarkdown/markdown/ast"
+ "github.com/gomarkdown/markdown/html"
+ "github.com/gomarkdown/markdown/parser"
+)
+
+var (
+ themeName = "gruvbox"
+ contentDir = "./content"
+ outputDir = "./output"
+)
+
+type Frontmatter struct {
+ Title string `yaml:"title"`
+ Date string `yaml:"date"`
+ Tags []string `yaml:"tags"`
+}
+
+type Page struct {
+ Title string
+ Content template.HTML
+ TOC []TOCItem
+}
+
+type TOCItem struct {
+ Level int
+ Text string
+ ID string
+}
+
+type PostSummary struct {
+ Title string
+ Slug string
+ Date string
+ Tags []string
+}
+
+type HomePage struct {
+ SiteTitle string `yaml:"siteTitle"`
+ AuthorName string `yaml:"authorName"`
+ AuthorRole string `yaml:"authorRole"`
+ AuthorBio string `yaml:"authorBio"`
+ Year int
+ Posts []PostSummary
+}
+
+var posts = make([]Post, 0)
+
+type Post struct {
+ Title string
+}
+
+func Build() {
+ summaries := make([]PostSummary, 0)
+
+ err := filepath.WalkDir(contentDir, func(path string, d fs.DirEntry, err error) error {
+ if err != nil {
+ return err
+ }
+
+ if d.IsDir() {
+ return nil
+ }
+
+ if strings.HasSuffix(d.Name(), ".md") {
+ fmt.Println("Processing:", path)
+
+ matter, htmlContent, toc := convertToHtml(path)
+ slug := strings.TrimSuffix(d.Name(), ".md")
+ summaries = append(summaries, PostSummary{
+ Title: matter.Title,
+ Slug: slug,
+ Date: matter.Date,
+ Tags: matter.Tags,
+ })
+
+ posts = append(posts, Post{Title: matter.Title})
+ // fmt.Println("Appended post", posts)
+ newPage := Page{
+ Title: matter.Title,
+ Content: template.HTML(htmlContent),
+ TOC: toc,
+ }
+
+ tmpl, err := template.ParseFiles("./themes/" + themeName + "/layout.html")
+ if err != nil {
+ log.Fatalf("Error parsing template: %s", err)
+ }
+
+ relPath, err := filepath.Rel(contentDir, path)
+ if err != nil {
+ log.Fatalf("Error computing relative path: %s", err)
+ }
+ // test.md -> test.html
+ // test.md -> test/index.html
+ outputFilePath := filepath.Join(outputDir, strings.Replace(relPath, ".md", "/index.html", 1))
+
+ err = os.MkdirAll(filepath.Dir(outputFilePath), 0o755)
+ if err != nil {
+ log.Fatalf("Error creating directories: %s", err)
+ }
+
+ outputFile, err := os.Create(outputFilePath)
+ if err != nil {
+ log.Fatalf("Error creating output file: %s", err)
+ }
+ defer outputFile.Close()
+
+ err = tmpl.Execute(outputFile, newPage)
+ if err != nil {
+ log.Fatalf("Error generating output content: %s", err)
+ }
+ }
+
+ return nil
+ })
+ if err != nil {
+ log.Fatalf("Error walking directory: %s", err)
+ }
+
+ fmt.Println("All files processed!")
+
+ fmt.Println(posts)
+
+ renderHomePage(summaries, outputDir)
+}
+
+func convertToHtml(path string) (Frontmatter, []byte, []TOCItem) {
+ md, err := os.ReadFile(path)
+ if err != nil {
+ log.Fatalf("Error reading %s: %s", path, err)
+ }
+
+ var matter Frontmatter
+ rest, err := frontmatter.Parse(strings.NewReader(string(md)), &matter)
+ if err != nil {
+ log.Fatalf("Error parsing frontmatter: %s", err)
+ }
+
+ extensions := parser.CommonExtensions | parser.AutoHeadingIDs
+ p := parser.NewWithExtensions(extensions)
+
+ doc := p.Parse(rest)
+
+ var toc []TOCItem
+
+ ast.WalkFunc(doc, func(node ast.Node, entering bool) ast.WalkStatus {
+ if heading, ok := node.(*ast.Heading); ok && entering {
+ text := extractText(heading)
+ id := string(heading.HeadingID)
+
+ toc = append(toc, TOCItem{
+ Level: heading.Level,
+ Text: text,
+ ID: id,
+ })
+ }
+ return ast.GoToNext
+ })
+
+ renderer := html.NewRenderer(html.RendererOptions{
+ Flags: html.CommonFlags,
+ })
+
+ output := markdown.Render(doc, renderer)
+
+ return matter, output, toc
+}
+
+func extractText(h *ast.Heading) string {
+ var text string
+ ast.WalkFunc(h, func(node ast.Node, entering bool) ast.WalkStatus {
+ if leaf, ok := node.(*ast.Text); ok && entering {
+ text += string(leaf.Literal)
+ }
+ return ast.GoToNext
+ })
+ return text
+}
+
+func renderHomePage(summaries []PostSummary, outputDir string) {
+ sort.Slice(summaries, func(i, j int) bool {
+ return summaries[i].Date > summaries[j].Date
+ })
+
+ for i, p := range summaries {
+ if t, err := time.Parse("2006-01-02", p.Date); err == nil {
+ summaries[i].Date = t.Format("Jan 2006")
+ }
+ }
+
+ config, err := os.ReadFile("config.yaml")
+ if err != nil {
+ panic(err)
+ }
+ var data HomePage
+ err = yaml.Unmarshal(config, &data)
+ data.Posts = summaries
+ data.Year = time.Now().Year()
+
+ if err != nil {
+ panic(err)
+ }
+
+ tmpl, err := template.ParseFiles("./themes/" + themeName + "/home.html")
+ if err != nil {
+ log.Fatalf("Error parsing home template: %s", err)
+ }
+
+ outPath := filepath.Join(outputDir, "index.html")
+ if err := os.MkdirAll(filepath.Dir(outPath), 0o755); err != nil {
+ log.Fatalf("Error creating output dir: %s", err)
+ }
+ f, err := os.Create(outPath)
+ if err != nil {
+ log.Fatalf("Error creating index.html: %s", err)
+ }
+ defer f.Close()
+
+ if err := tmpl.Execute(f, data); err != nil {
+ log.Fatalf("Error rendering home page: %s", err)
+ }
+ fmt.Println("Home page written to", outPath)
+}