package staticsite import ( "bytes" "fmt" "os" "path/filepath" "strings" "github.com/yuin/goldmark" ) // RenderMarkdownFiles walks the directory and converts all .md files to .html. // The original .md file is kept alongside the generated .html file. func RenderMarkdownFiles(dir string) error { md := goldmark.New() return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if info.IsDir() { return nil } ext := strings.ToLower(filepath.Ext(path)) if ext != ".md" && ext != ".markdown" { return nil } src, err := os.ReadFile(path) if err != nil { return fmt.Errorf("read %s: %w", path, err) } var buf bytes.Buffer if err := md.Convert(src, &buf); err != nil { return fmt.Errorf("render %s: %w", path, err) } html := wrapHTML(extractTitle(src), buf.String()) htmlPath := strings.TrimSuffix(path, filepath.Ext(path)) + ".html" if err := os.WriteFile(htmlPath, []byte(html), 0o644); err != nil { return fmt.Errorf("write %s: %w", htmlPath, err) } return nil }) } // extractTitle finds the first # heading in markdown content. func extractTitle(src []byte) string { for _, line := range strings.Split(string(src), "\n") { trimmed := strings.TrimSpace(line) if strings.HasPrefix(trimmed, "# ") { return strings.TrimPrefix(trimmed, "# ") } } return "Page" } // wrapHTML wraps rendered markdown in a minimal HTML document. func wrapHTML(title, body string) string { return fmt.Sprintf(` %s %s `, title, body) }