diff options
| author | Himanshu Sardana <himanshusardana2005@gmail.com> | 2026-03-25 09:21:19 +0000 |
|---|---|---|
| committer | Himanshu Sardana <himanshusardana2005@gmail.com> | 2026-03-25 09:21:19 +0000 |
| commit | af0332e7d805f7a4cb1086b66f61ad706a082768 (patch) | |
| tree | 24622ad88fa1edf636e5c7fac5ac3f5d8e002e6d /internal | |
| parent | 7cfdc43226930b21bae2e02581672a338a9c0789 (diff) | |
refactor: split into cmd/internal
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/build/build.go | 240 |
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) +} |
