package main import ( "fmt" "html/template" "log" "os" "strings" "github.com/alecthomas/chroma/v2" "github.com/alecthomas/chroma/v2/formatters/html" "github.com/alecthomas/chroma/v2/lexers" "github.com/alecthomas/chroma/v2/styles" "github.com/niklasfasching/go-org/org" ) // renderHome renders the home page of the website. // It processes the index template, executes it with the provided posts and tags, // and writes the resulting HTML to the build directory. // Parameters: // - posts: A slice of Post structs representing the blog posts. // - tags: A slice of strings representing the tags. // - css: A string containing the compiled CSS styles. // // Returns: // - An error if any step of the process fails, otherwise nil. func renderHome(posts []Post, tags []string, css string) error { indexTmpl, _ := template.ParseFiles("templates/parts/index.html") var indexContentBuf strings.Builder indexData := struct { Posts []Post }{ Posts: posts, } _ = indexTmpl.Execute(&indexContentBuf, indexData) // Parse the index.html template tmpl, err := template.ParseFiles("templates/layout.html", "templates/parts/header.html") if err != nil { return fmt.Errorf("error parsing template: %v", err) } // Create a buffer to hold the template output var buf strings.Builder // Execute the template with the necessary data data := struct { Css template.CSS Content template.HTML Hero template.HTML Tags []string ShowSidebar bool }{ Css: template.CSS(css), Content: template.HTML(indexContentBuf.String()), Hero: template.HTML("
"), Tags: tags, ShowSidebar: true, } if err := tmpl.Execute(&buf, data); err != nil { return fmt.Errorf("error executing template: %v", err) } // Create the build directory if it doesn't exist if err := os.MkdirAll("build", os.ModePerm); err != nil { return fmt.Errorf("error creating directory: %v", err) } // Write the HTML content to the index.html file in the build directory if err := os.WriteFile("build/index.html", []byte(buf.String()), 0644); err != nil { return fmt.Errorf("error writing HTML file: %v", err) } log.Println("Wrote build/index.html") return nil } // highlightCodeBlock highlights a code block using the specified language and parameters. // It uses the chroma library to tokenize and format the code block. // Parameters: // - source: The source code to highlight. // - lang: The programming language of the code. // - inline: Whether the code block is inline or not. // - params: Additional parameters for highlighting, such as highlighted lines. // // Returns: // - A string containing the highlighted code block in HTML format. func highlightCodeBlock(source, lang string, inline bool, params map[string]string) string { var w strings.Builder l := lexers.Get(lang) if l == nil { l = lexers.Fallback } l = chroma.Coalesce(l) it, _ := l.Tokenise(nil, source) options := []html.Option{} if params[":hl_lines"] != "" { ranges := org.ParseRanges(params[":hl_lines"]) if ranges != nil { options = append(options, html.HighlightLines(ranges)) } } _ = html.New(options...).Format(&w, styles.Get("dracula"), it) if inline { return `
` + "\n" + w.String() + "\n" + `
` } return `
` + "\n" + w.String() + "\n" + `
` } // renderPost renders a single blog post to an HTML file. // It processes the post content, applies syntax highlighting to code blocks, // and writes the resulting HTML to the build directory. // Parameters: // - post: The Post struct representing the blog post. // - css: A string containing the compiled CSS styles. // - tags: A slice of strings representing the tags. // // Returns: // - An error if any step of the process fails, otherwise nil. func renderPost(post Post, css string, tags []string) error { htmlFilePath := "build/posts/" + post.Slug + ".html" render := func(w org.Writer) string { out, err := post.Content.Write(w) if err != nil { log.Fatal(err) } return out } renderer := org.NewHTMLWriter() renderer.HighlightCodeBlock = highlightCodeBlock htmlContent := render(renderer) if err := os.MkdirAll("build/posts", os.ModePerm); err != nil { log.Fatal("Error creating directory:", err) return err } // Generate the new file path for the HTML output tmpl, err := template.ParseFiles("templates/layout.html", "templates/parts/header.html") if err != nil { log.Fatal("Error parsing template:", err) return err } hero := func(post Post) template.HTML { if post.Hero != "/medias/" && post.Hero != "/medias/none" { return template.HTML(fmt.Sprintf("", post.Hero)) } else { return template.HTML("") } } // Create a buffer to hold the template output var buf strings.Builder // Execute the template with the necessary data data := struct { Content template.HTML Css template.CSS Hero template.HTML Tags []string ShowSidebar bool }{ Content: template.HTML(htmlContent), Css: template.CSS(css), Hero: hero(post), Tags: tags, ShowSidebar: false, } if err := tmpl.Execute(&buf, data); err != nil { log.Fatal("Error executing template:", err) return err } // Write the HTML content to the new file if err := os.WriteFile(htmlFilePath, []byte(buf.String()), 0644); err != nil { log.Fatal("Error writing HTML file:", err) return err } log.Println("Wrote", htmlFilePath) return nil } // renderTagPage renders a tag page for a specific tag. // It processes the tag page template, executes it with the provided tag and posts, // and writes the resulting HTML to the build directory. // Parameters: // - tag: The tag for which the page is being rendered. // - posts: A slice of Post structs representing the blog posts associated with the tag. // - tags: A slice of strings representing all tags. // - css: A string containing the compiled CSS styles. // // Returns: // - An error if any step of the process fails, otherwise nil. func renderTagPage(tag string, posts []Post, tags []string, css string) error { htmlFilePath := "build/tags/" + tag + ".html" if err := os.MkdirAll("build/tags", os.ModePerm); err != nil { log.Fatal("Error creating directory:", err) return err } tagPageTmpl, _ := template.ParseFiles("templates/parts/tagPage.html") var tagPageContentBuf strings.Builder tagPageData := struct { Tag string Posts []Post }{ Tag: tag, Posts: posts, } _ = tagPageTmpl.Execute(&tagPageContentBuf, tagPageData) // Generate the new file path for the HTML output tmpl, err := template.ParseFiles("templates/layout.html", "templates/parts/header.html") if err != nil { log.Fatal("Error parsing template:", err) return err } // Create a buffer to hold the template output var buf strings.Builder // Execute the template with the necessary data data := struct { Content template.HTML Css template.CSS Hero template.HTML Tags []string ShowSidebar bool Tag string }{ Content: template.HTML(tagPageContentBuf.String()), Css: template.CSS(css), Hero: template.HTML(""), Tags: tags, ShowSidebar: false, Tag: tag, } if err := tmpl.Execute(&buf, data); err != nil { log.Fatal("Error executing template:", err) return err } // Write the HTML content to the new file if err := os.WriteFile(htmlFilePath, []byte(buf.String()), 0644); err != nil { log.Fatal("Error writing HTML file:", err) return err } log.Println("Wrote", htmlFilePath) return nil }