mirror of
https://github.com/sbrow/thor.git
synced 2026-08-26 11:23:32 -04:00
feat: Added sitemap and RSS feed.
This commit is contained in:
@@ -5,8 +5,6 @@
|
|||||||
should be Hugo / go.
|
should be Hugo / go.
|
||||||
- [ ] Deal with the favicon
|
- [ ] Deal with the favicon
|
||||||
- [ ] Icon support.
|
- [ ] Icon support.
|
||||||
- [ ] RSS
|
|
||||||
- [ ] sitemap
|
|
||||||
- [ ] robots.txt?
|
- [ ] robots.txt?
|
||||||
- [ ] Evaluate Tufte CSS — borrow sidenote CSS or replace TailwindCSS entirely
|
- [ ] Evaluate Tufte CSS — borrow sidenote CSS or replace TailwindCSS entirely
|
||||||
- Option B: Steal Tufte's sidenote/margin-note CSS (adapt for dark theme), keep TailwindCSS
|
- Option B: Steal Tufte's sidenote/margin-note CSS (adapt for dark theme), keep TailwindCSS
|
||||||
|
|||||||
@@ -0,0 +1,149 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "core:fmt"
|
||||||
|
import "core:strings"
|
||||||
|
|
||||||
|
WEEKDAYS: [7]string = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}
|
||||||
|
|
||||||
|
generate_rss :: proc(
|
||||||
|
pages: []Page,
|
||||||
|
site_title: string,
|
||||||
|
site_desc: string,
|
||||||
|
base_url: string,
|
||||||
|
) -> string {
|
||||||
|
parts: [dynamic]string
|
||||||
|
defer delete(parts)
|
||||||
|
|
||||||
|
append(&parts, fmt.aprintf(
|
||||||
|
`<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||||
|
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||||
|
<channel>
|
||||||
|
<title>%s</title>
|
||||||
|
<link>%s/</link>
|
||||||
|
<description>%s</description>
|
||||||
|
<language>en-us</language>
|
||||||
|
<atom:link href="%s/index.xml" rel="self" type="application/rss+xml"/>`,
|
||||||
|
xml_escape(site_title),
|
||||||
|
base_url,
|
||||||
|
xml_escape(site_desc),
|
||||||
|
base_url,
|
||||||
|
))
|
||||||
|
|
||||||
|
for page in pages {
|
||||||
|
if page.type == .Home {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
pub_date := "Mon, 01 Jan 0001 00:00:00 +0000"
|
||||||
|
if page.date != "" {
|
||||||
|
pub_date = format_rfc822(page.date)
|
||||||
|
}
|
||||||
|
|
||||||
|
append(&parts, fmt.aprintf(
|
||||||
|
`<item>
|
||||||
|
<title>%s</title>
|
||||||
|
<link>%s%s</link>
|
||||||
|
<pubDate>%s</pubDate>
|
||||||
|
<guid>%s%s</guid>
|
||||||
|
<description>%s</description>
|
||||||
|
</item>
|
||||||
|
`,
|
||||||
|
xml_escape(page.title),
|
||||||
|
base_url,
|
||||||
|
page.permalink,
|
||||||
|
pub_date,
|
||||||
|
base_url,
|
||||||
|
page.permalink,
|
||||||
|
xml_escape(page.body_html),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
append(&parts, "</channel>\n</rss>")
|
||||||
|
|
||||||
|
return strings.join(parts[:], "")
|
||||||
|
}
|
||||||
|
|
||||||
|
generate_sitemap :: proc(pages: []Page, base_url: string) -> string {
|
||||||
|
parts: [dynamic]string
|
||||||
|
defer delete(parts)
|
||||||
|
|
||||||
|
append(
|
||||||
|
&parts,
|
||||||
|
`<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||||
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">
|
||||||
|
`,
|
||||||
|
)
|
||||||
|
|
||||||
|
for page in pages {
|
||||||
|
lastmod := ""
|
||||||
|
if page.date != "" {
|
||||||
|
lastmod = fmt.aprintf("<lastmod>%s</lastmod>", page.date)
|
||||||
|
}
|
||||||
|
append(&parts, fmt.aprintf(
|
||||||
|
"<url><loc>%s%s</loc>%s</url>\n",
|
||||||
|
base_url,
|
||||||
|
page.permalink,
|
||||||
|
lastmod,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Posts list page
|
||||||
|
posts_lastmod := ""
|
||||||
|
for page in pages {
|
||||||
|
if page.type == .Post && page.date > posts_lastmod {
|
||||||
|
posts_lastmod = page.date
|
||||||
|
}
|
||||||
|
}
|
||||||
|
posts_lm := ""
|
||||||
|
if posts_lastmod != "" {
|
||||||
|
posts_lm = fmt.aprintf("<lastmod>%s</lastmod>", posts_lastmod)
|
||||||
|
}
|
||||||
|
append(&parts, fmt.aprintf("<url><loc>%s/posts/</loc>%s</url>\n", base_url, posts_lm))
|
||||||
|
|
||||||
|
append(&parts, "</urlset>")
|
||||||
|
|
||||||
|
return strings.join(parts[:], "")
|
||||||
|
}
|
||||||
|
|
||||||
|
format_rfc822 :: proc(iso: string) -> string {
|
||||||
|
if len(iso) < 19 {
|
||||||
|
return iso
|
||||||
|
}
|
||||||
|
|
||||||
|
year := (int(iso[0]) - 0x30) * 1000 + (int(iso[1]) - 0x30) * 100 +
|
||||||
|
(int(iso[2]) - 0x30) * 10 + (int(iso[3]) - 0x30)
|
||||||
|
month := (int(iso[5]) - 0x30) * 10 + (int(iso[6]) - 0x30)
|
||||||
|
day := (int(iso[8]) - 0x30) * 10 + (int(iso[9]) - 0x30)
|
||||||
|
time := iso[11:19]
|
||||||
|
|
||||||
|
// Timezone: -04:00 → -0400
|
||||||
|
tz := "+0000"
|
||||||
|
if len(iso) >= 25 && (iso[19] == '+' || iso[19] == '-') {
|
||||||
|
tz = fmt.tprintf("%c%s%s", iso[19], iso[20:22], iso[23:25])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sakamoto's method for day of week (0=Sunday)
|
||||||
|
t := [12]int{0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4}
|
||||||
|
y := year
|
||||||
|
if month < 3 {
|
||||||
|
y -= 1
|
||||||
|
}
|
||||||
|
dow := (y + y / 4 - y / 100 + y / 400 + t[month - 1] + day) % 7
|
||||||
|
|
||||||
|
return fmt.aprintf(
|
||||||
|
"%s, %d %s %d %s %s",
|
||||||
|
WEEKDAYS[dow],
|
||||||
|
day,
|
||||||
|
MONTHS[month - 1],
|
||||||
|
year,
|
||||||
|
time,
|
||||||
|
tz,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
xml_escape :: proc(s: string) -> string {
|
||||||
|
r, _ := strings.replace_all(s, "&", "&")
|
||||||
|
r, _ = strings.replace_all(r, "<", "<")
|
||||||
|
r, _ = strings.replace_all(r, ">", ">")
|
||||||
|
return r
|
||||||
|
}
|
||||||
@@ -20,7 +20,7 @@ main :: proc() {
|
|||||||
|
|
||||||
pages := walk_content(opt.content_dir, opt.drafts)
|
pages := walk_content(opt.content_dir, opt.drafts)
|
||||||
|
|
||||||
render_site(pages, opt.content_dir, opt.output_dir)
|
render_site(pages, opt.content_dir, opt.output_dir, opt.base_url)
|
||||||
}
|
}
|
||||||
|
|
||||||
load_default_options :: proc(opt: ^Options) {
|
load_default_options :: proc(opt: ^Options) {
|
||||||
@@ -31,6 +31,10 @@ load_default_options :: proc(opt: ^Options) {
|
|||||||
if opt.output_dir == "" {
|
if opt.output_dir == "" {
|
||||||
opt.output_dir = "./public"
|
opt.output_dir = "./public"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opt.base_url == "" {
|
||||||
|
opt.base_url = "http://localhost:8080"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
print_summary :: proc(pages: []Page) {
|
print_summary :: proc(pages: []Page) {
|
||||||
|
|||||||
+11
-1
@@ -9,7 +9,7 @@ MONTHS: [12]string = {
|
|||||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
|
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
|
||||||
}
|
}
|
||||||
|
|
||||||
render_site :: proc(pages: []Page, content_path: string, output_dir: string) {
|
render_site :: proc(pages: []Page, content_path: string, output_dir: string, base_url: string) {
|
||||||
sort_pages_by_date(pages)
|
sort_pages_by_date(pages)
|
||||||
|
|
||||||
// Find home page and extract site title
|
// Find home page and extract site title
|
||||||
@@ -48,6 +48,16 @@ render_site :: proc(pages: []Page, content_path: string, output_dir: string) {
|
|||||||
posts_html := render_posts_html(pages, site_title)
|
posts_html := render_posts_html(pages, site_title)
|
||||||
write_page(output_dir, "/posts/", posts_html)
|
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)
|
||||||
|
|
||||||
total := len(pages) + 1
|
total := len(pages) + 1
|
||||||
if !has_home {
|
if !has_home {
|
||||||
total += 1
|
total += 1
|
||||||
|
|||||||
Reference in New Issue
Block a user