package main import "core:fmt" import "core:os" import "core:strings" MONTHS: [12]string = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", } render_site :: proc(pages: []Page, content_path: string, output_dir: string, base_url: string) { sort_pages_by_date(pages) // Find home page and extract site title home: Page has_home := false site_title := "Site" for page in pages { if page.type == .Home { home = page has_home = true site_title = page.title break } } // Render individual content pages (skip home) for page in pages { if page.type == .Home { continue } html := render_page_html(page, site_title) write_page(output_dir, page.permalink, html) } // Render home page if has_home { home_html := render_home_html(home, pages, site_title) write_file(fmt.tprintf("%s/index.html", output_dir), home_html) } // Render posts list page posts_html := render_posts_html(pages, site_title) write_page(output_dir, "/posts/", posts_html) // Generate RSS feed if has_home { rss := generate_rss(pages, site_title, home.description, base_url) write_file(fmt.tprintf("%s/index.xml", output_dir), rss) } // Generate sitemap sitemap := generate_sitemap(pages, base_url) write_file(fmt.tprintf("%s/sitemap.xml", output_dir), sitemap) // Copy static assets (avatar, favicon, etc.) copy_static_assets(content_path, output_dir) total := len(pages) + 1 if !has_home { total += 1 } fmt.printfln("Rendered %d pages to %s", total, output_dir) } render_page_html :: proc(page: Page, site_title: string) -> string { comments := "" if page.type == .Post { comments = ` ` } body := fmt.aprintf( `

%s

%s %s
%s
`, page.title, render_date(page), page.body_html, comments, ) title := fmt.tprintf("%s | %s", page.title, site_title) return render_chrome(title, body) } render_home_html :: proc(home: Page, pages: []Page, site_title: string) -> string { items: [dynamic]string defer delete(items) for page in pages { if page.type == .Home { continue } append(&items, render_post_item(page)) } post_list := strings.join(items[:], "\n") body := fmt.aprintf( `
%s
`, home.body_html, post_list, ) return render_chrome(site_title, body) } render_posts_html :: proc(pages: []Page, site_title: string) -> string { parts: [dynamic]string defer delete(parts) append(&parts, "
\n") append(&parts, `

Posts

` + "\n") current_year := "" open := false for page in pages { if page.type != .Post { continue } year := get_year(page.date) if year != current_year { if open { append(&parts, " \n \n") } open = true current_year = year append( &parts, fmt.aprintf("
\n

%s

\n
\n \n
\n") } append(&parts, "
\n") body := strings.join(parts[:], "") title := fmt.tprintf("Posts | %s", site_title) return render_chrome(title, body) } render_chrome :: proc(page_title: string, body: string) -> string { header := fmt.aprintf(HEADER, ICON_HOME) return fmt.aprintf( ` %s %s%s `, page_title, header, body, ICON_CHEVRON_UP, ICON_GITHUB, ICON_RSS, ) } HEADER :: `
` render_post_item :: proc(page: Page) -> string { date_html := "" if page.date != "" { date_html = fmt.aprintf( "", page.date, format_date(page.date), ) } star := "" if page.is_starred { star = ICON_STAR } return fmt.aprintf( `
  • %s%s%s
  • `, page.permalink, page.title, star, date_html, ) } render_date :: proc(page: Page) -> string { if page.date == "" { return "" } return fmt.aprintf(` ` + "\n", page.date, format_date(page.date)) } format_date :: proc(iso: string) -> string { if len(iso) < 10 { return iso } year := iso[:4] month_num := (int(iso[5]) - 0x30) * 10 + (int(iso[6]) - 0x30) day_num := (int(iso[8]) - 0x30) * 10 + (int(iso[9]) - 0x30) if month_num < 1 || month_num > 12 { return iso[:10] } return fmt.aprintf("%d %s %s", day_num, MONTHS[month_num - 1], year) } get_year :: proc(iso: string) -> string { if len(iso) < 4 { return "" } return iso[:4] } sort_pages_by_date :: proc(pages: []Page) { for i in 1..= 0 && pages[j].date < key.date { pages[j + 1] = pages[j] j -= 1 } pages[j + 1] = key } } write_page :: proc(output_dir: string, permalink: string, html: string) { rel := permalink if len(rel) > 0 && rel[0] == '/' { rel = rel[1:] } dir := fmt.tprintf("%s/%s", output_dir, rel) if err := os.make_directory_all(dir); err != nil && err != .Exist { fmt.eprintfln("thor: cannot create %s: %v", dir, err) return } file_path := fmt.tprintf("%s/index.html", dir) write_file(file_path, html) } write_file :: proc(path: string, html: string) { if err := os.write_entire_file_from_string(path, html); err != nil { fmt.eprintfln("thor: cannot write %s: %v", path, err) } }