feat: integrate tag pages into sitemap
Thread tag names from collect_tags() through generate_sitemap_file to generate_sitemap, appending /tags/<tag>.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.
This commit is contained in:
@@ -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/<tag>.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
|
||||
|
||||
|
||||
31
src/main.rs
31
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<String> = 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<String, Vec<ContentContext>>,
|
||||
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 {
|
||||
|
||||
@@ -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<SitemapEntry> = 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("<url>").count(), 2);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user