|
| 1 | +using System.Diagnostics; |
1 | 2 | using System.Net; |
2 | 3 | using System.Text; |
3 | 4 | using System.Text.RegularExpressions; |
@@ -117,6 +118,7 @@ static int Main(string[] args) |
117 | 118 |
|
118 | 119 | GenerateComponentPages(categories, pagesPath, xmlDocs, typeMap, mdDir); |
119 | 120 | GenerateIndex(categories, Path.Combine(outputDir, "llms.txt"), xmlDocs); |
| 121 | + GenerateSitemap(categories, pagesPath, outputDir); |
120 | 122 |
|
121 | 123 | var apiCount = Directory.Exists(Path.Combine(mdDir, "api")) ? Directory.GetFiles(Path.Combine(mdDir, "api"), "*.md").Length : 0; |
122 | 124 | var pageCount = Directory.GetFiles(mdDir, "*.md").Length; |
@@ -1358,4 +1360,140 @@ static string CleanExampleFile(string content) |
1358 | 1360 |
|
1359 | 1361 | return result.Trim(); |
1360 | 1362 | } |
| 1363 | + |
| 1364 | + // ── Sitemap generation ────────────────────────────────────────────── |
| 1365 | + |
| 1366 | + static void GenerateSitemap(List<ExampleNode> categories, string pagesPath, string outputDir) |
| 1367 | + { |
| 1368 | + var routeFileMap = BuildRouteFileMap(pagesPath); |
| 1369 | + var gitRoot = FindGitRoot(pagesPath); |
| 1370 | + |
| 1371 | + var urls = new List<(string Url, string Lastmod)>(); |
| 1372 | + |
| 1373 | + foreach (var category in categories) |
| 1374 | + { |
| 1375 | + CollectSitemapUrls(category, routeFileMap, gitRoot, urls); |
| 1376 | + } |
| 1377 | + |
| 1378 | + var sitemapPath = Path.Combine(outputDir, "sitemap.xml"); |
| 1379 | + WriteSitemap(sitemapPath, urls); |
| 1380 | + |
| 1381 | + var robotsPath = Path.Combine(outputDir, "robots.txt"); |
| 1382 | + WriteRobotsTxt(robotsPath); |
| 1383 | + |
| 1384 | + Console.WriteLine($"Generated sitemap.xml ({urls.Count} URLs) and robots.txt in: {outputDir}"); |
| 1385 | + } |
| 1386 | + |
| 1387 | + static void CollectSitemapUrls(ExampleNode node, Dictionary<string, string> routeFileMap, string gitRoot, List<(string Url, string Lastmod)> urls) |
| 1388 | + { |
| 1389 | + if (!string.IsNullOrEmpty(node.Path)) |
| 1390 | + { |
| 1391 | + var route = node.Path.TrimStart('/'); |
| 1392 | + var url = string.IsNullOrEmpty(route) ? BaseUrl + "/" : $"{BaseUrl}/{route}"; |
| 1393 | + |
| 1394 | + string lastmod = null; |
| 1395 | + if (gitRoot != null && routeFileMap.TryGetValue(route, out var filePath)) |
| 1396 | + { |
| 1397 | + lastmod = GetGitLastModified(gitRoot, filePath); |
| 1398 | + } |
| 1399 | + |
| 1400 | + urls.Add((url, lastmod)); |
| 1401 | + } |
| 1402 | + |
| 1403 | + if (node.Children != null) |
| 1404 | + { |
| 1405 | + foreach (var child in node.Children) |
| 1406 | + { |
| 1407 | + CollectSitemapUrls(child, routeFileMap, gitRoot, urls); |
| 1408 | + } |
| 1409 | + } |
| 1410 | + } |
| 1411 | + |
| 1412 | + static Dictionary<string, string> BuildRouteFileMap(string pagesPath) |
| 1413 | + { |
| 1414 | + var map = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); |
| 1415 | + foreach (var file in Directory.GetFiles(pagesPath, "*.razor", SearchOption.AllDirectories)) |
| 1416 | + { |
| 1417 | + var content = File.ReadAllText(file); |
| 1418 | + var match = Regex.Match(content, @"@page\s+""(/[^""]*)""\s*$", RegexOptions.Multiline); |
| 1419 | + if (match.Success) |
| 1420 | + map[match.Groups[1].Value.TrimStart('/')] = file; |
| 1421 | + } |
| 1422 | + return map; |
| 1423 | + } |
| 1424 | + |
| 1425 | + static string FindGitRoot(string startDir) |
| 1426 | + { |
| 1427 | + var dir = Path.GetFullPath(startDir); |
| 1428 | + while (dir != null) |
| 1429 | + { |
| 1430 | + if (Directory.Exists(Path.Combine(dir, ".git"))) |
| 1431 | + return dir; |
| 1432 | + dir = Path.GetDirectoryName(dir); |
| 1433 | + } |
| 1434 | + return null; |
| 1435 | + } |
| 1436 | + |
| 1437 | + static string GetGitLastModified(string repoRoot, string filePath) |
| 1438 | + { |
| 1439 | + var relativePath = Path.GetRelativePath(repoRoot, filePath); |
| 1440 | + try |
| 1441 | + { |
| 1442 | + var psi = new ProcessStartInfo |
| 1443 | + { |
| 1444 | + FileName = "git", |
| 1445 | + Arguments = $"log --format=%aI -1 -- \"{relativePath}\"", |
| 1446 | + WorkingDirectory = repoRoot, |
| 1447 | + RedirectStandardOutput = true, |
| 1448 | + RedirectStandardError = true, |
| 1449 | + UseShellExecute = false, |
| 1450 | + CreateNoWindow = true |
| 1451 | + }; |
| 1452 | + |
| 1453 | + using var process = Process.Start(psi); |
| 1454 | + var output = process.StandardOutput.ReadToEnd().Trim(); |
| 1455 | + process.WaitForExit(); |
| 1456 | + |
| 1457 | + if (process.ExitCode == 0 && !string.IsNullOrEmpty(output)) |
| 1458 | + { |
| 1459 | + if (DateTimeOffset.TryParse(output, out var dto)) |
| 1460 | + return dto.ToString("yyyy-MM-dd"); |
| 1461 | + } |
| 1462 | + } |
| 1463 | + catch |
| 1464 | + { |
| 1465 | + // git not available — skip lastmod |
| 1466 | + } |
| 1467 | + |
| 1468 | + return null; |
| 1469 | + } |
| 1470 | + |
| 1471 | + static void WriteSitemap(string filePath, List<(string Url, string Lastmod)> urls) |
| 1472 | + { |
| 1473 | + var sb = new StringBuilder(); |
| 1474 | + sb.AppendLine("<?xml version=\"1.0\" encoding=\"utf-8\"?>"); |
| 1475 | + sb.AppendLine("<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">"); |
| 1476 | + |
| 1477 | + foreach (var (url, lastmod) in urls) |
| 1478 | + { |
| 1479 | + sb.AppendLine(" <url>"); |
| 1480 | + sb.AppendLine($" <loc>{url}</loc>"); |
| 1481 | + if (!string.IsNullOrEmpty(lastmod)) |
| 1482 | + sb.AppendLine($" <lastmod>{lastmod}</lastmod>"); |
| 1483 | + sb.AppendLine(" </url>"); |
| 1484 | + } |
| 1485 | + |
| 1486 | + sb.AppendLine("</urlset>"); |
| 1487 | + File.WriteAllText(filePath, sb.ToString(), new UTF8Encoding(false)); |
| 1488 | + } |
| 1489 | + |
| 1490 | + static void WriteRobotsTxt(string filePath) |
| 1491 | + { |
| 1492 | + var sb = new StringBuilder(); |
| 1493 | + sb.AppendLine("User-agent: *"); |
| 1494 | + sb.AppendLine($"Sitemap: {BaseUrl}/sitemap.xml"); |
| 1495 | + sb.AppendLine("Disallow: /llms.txt"); |
| 1496 | + sb.AppendLine("Disallow: /*.md$"); |
| 1497 | + File.WriteAllText(filePath, sb.ToString(), new UTF8Encoding(false)); |
| 1498 | + } |
1361 | 1499 | } |
0 commit comments