From 7b3373295ba210c738ed1d5a59eec7023d7b97e6 Mon Sep 17 00:00:00 2001 From: Timothy DeHerrera Date: Sun, 15 Feb 2026 09:15:31 -0700 Subject: [PATCH] feat: integrate tag pages into sitemap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thread tag names from collect_tags() through generate_sitemap_file to generate_sitemap, appending /tags/.html entries to sitemap.xml output. Refactor: hoist collect_tags into run(), rename generate_tag_pages to write_tag_pages. Test suite: 83 → 84, phase 3 complete. --- docs/plans/api-stabilization.md | 4 ++-- src/main.rs | 31 +++++++++++++++---------------- src/sitemap.rs | 27 +++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 18 deletions(-) diff --git a/docs/plans/api-stabilization.md b/docs/plans/api-stabilization.md index efc98c6..be33667 100644 --- a/docs/plans/api-stabilization.md +++ b/docs/plans/api-stabilization.md @@ -131,10 +131,10 @@ Items validated by codebase investigation: - [x] Collect all unique tags across content items during build - [x] Create `tags/default.html` template in `docs/templates/` - [x] Generate `/tags/.html` for each unique tag with list of tagged items - - [ ] Add tag listing page entries to sitemap (if enabled) + - [x] Add tag listing page entries to sitemap (if enabled) - [x] Add tests: 404 page generation - [x] Add tests: tag listing page generation (correct paths, correct items per tag) - - [ ] End-to-end: build `docs/` site and verify all outputs + - [x] End-to-end: build `docs/` site and verify all outputs ## Verification diff --git a/src/main.rs b/src/main.rs index c1365e8..e9cabbc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -200,12 +200,16 @@ fn run(config_path: &Path) -> Result<()> { generate_404(page_404, &output_dir, &config, &manifest.nav, &engine)?; } - // 6. Generate tag listing pages - generate_tag_pages(&output_dir, &content_dir, &config, &manifest, &engine)?; + // 6. Collect tags and generate tag listing pages + let tags = collect_tags(&manifest.sections, &manifest.pages, &content_dir, &config); + if !tags.is_empty() { + write_tag_pages(&output_dir, &tags, &config, &manifest.nav, &engine)?; + } // 7. Generate sitemap (if enabled) + let tag_names: Vec = tags.keys().cloned().collect(); if config.sitemap.enabled { - generate_sitemap_file(&output_dir, &manifest, &config, &content_dir)?; + generate_sitemap_file(&output_dir, &manifest, &config, &content_dir, &tag_names)?; } // 8. Generate alias redirects @@ -242,11 +246,12 @@ fn generate_sitemap_file( manifest: &content::SiteManifest, config: &config::SiteConfig, content_dir: &Path, + tag_names: &[String], ) -> Result<()> { let out_path = output_dir.join("sitemap.xml"); eprintln!("generating: {}", out_path.display()); - let sitemap_xml = sitemap::generate_sitemap(manifest, config, content_dir); + let sitemap_xml = sitemap::generate_sitemap(manifest, config, content_dir, tag_names); fs::write(&out_path, sitemap_xml).map_err(|e| Error::WriteFile { path: out_path.clone(), @@ -387,29 +392,23 @@ fn collect_tags( tags } -/// Generate tag listing pages for all unique tags across content. -fn generate_tag_pages( +/// Write tag listing pages from pre-collected tag data. +fn write_tag_pages( output_dir: &Path, - content_dir: &Path, + tags: &BTreeMap>, config: &config::SiteConfig, - manifest: &content::SiteManifest, + nav: &[NavItem], engine: &TemplateEngine, ) -> Result<()> { - let tags = collect_tags(&manifest.sections, &manifest.pages, content_dir, config); - - if tags.is_empty() { - return Ok(()); - } - let tags_dir = output_dir.join("tags"); fs::create_dir_all(&tags_dir).map_err(|e| Error::CreateDir { path: tags_dir.clone(), source: e, })?; - for (tag, items) in &tags { + for (tag, items) in tags { let page_path = format!("/tags/{}.html", tag); - let html = engine.render_tag_page(tag, items, &page_path, config, &manifest.nav)?; + let html = engine.render_tag_page(tag, items, &page_path, config, nav)?; let out_path = tags_dir.join(format!("{}.html", tag)); fs::write(&out_path, html).map_err(|e| Error::WriteFile { diff --git a/src/sitemap.rs b/src/sitemap.rs index cc3242f..40d9c49 100644 --- a/src/sitemap.rs +++ b/src/sitemap.rs @@ -24,6 +24,7 @@ pub fn generate_sitemap( manifest: &SiteManifest, config: &SiteConfig, content_root: &Path, + tag_names: &[String], ) -> String { let base_url = config.base_url.trim_end_matches('/'); let mut entries = Vec::new(); @@ -63,6 +64,14 @@ pub fn generate_sitemap( }); } + // Tag listing pages + for tag in tag_names { + entries.push(SitemapEntry { + loc: format!("{}/tags/{}.html", base_url, tag), + lastmod: None, + }); + } + build_sitemap_xml(&entries) } @@ -165,4 +174,22 @@ mod tests { assert!(xml.contains("&")); assert!(!xml.contains("?q=foo&bar")); // Raw & should not appear } + + #[test] + fn test_sitemap_includes_tag_pages() { + let tag_names = vec!["rust".to_string(), "web".to_string()]; + let entries: Vec = tag_names + .iter() + .map(|tag| SitemapEntry { + loc: format!("https://example.com/tags/{}.html", tag), + lastmod: None, + }) + .collect(); + + let xml = build_sitemap_xml(&entries); + + assert!(xml.contains("https://example.com/tags/rust.html")); + assert!(xml.contains("https://example.com/tags/web.html")); + assert_eq!(xml.matches("").count(), 2); + } }