next-sitemap.config.cjs 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. const path = require('path');
  2. const fs = require('fs');
  3. const matter = require('gray-matter');
  4. /**
  5. * Get published blog posts for sitemap
  6. * Note: This runs at build time, so recently-scheduled posts may lag
  7. */
  8. function getPublishedBlogPosts() {
  9. const BLOG_DIR = path.join(process.cwd(), 'src/content/blog');
  10. if (!fs.existsSync(BLOG_DIR)) {
  11. return [];
  12. }
  13. const files = fs.readdirSync(BLOG_DIR).filter(f => f.endsWith('.md'));
  14. const posts = [];
  15. // Get current time in PT for publish check
  16. const formatter = new Intl.DateTimeFormat('en-US', {
  17. timeZone: 'America/Los_Angeles',
  18. year: 'numeric',
  19. month: '2-digit',
  20. day: '2-digit',
  21. hour: '2-digit',
  22. minute: '2-digit',
  23. hour12: false,
  24. });
  25. const parts = formatter.formatToParts(new Date());
  26. const get = (type) => parts.find(p => p.type === type)?.value ?? '';
  27. const nowDate = `${get('year')}-${get('month')}-${get('day')}`;
  28. const nowMinutes = parseInt(get('hour')) * 60 + parseInt(get('minute'));
  29. for (const file of files) {
  30. const filepath = path.join(BLOG_DIR, file);
  31. const raw = fs.readFileSync(filepath, 'utf8');
  32. const { data } = matter(raw);
  33. // Check if post is published
  34. if (data.status !== 'published') continue;
  35. // Parse publish time
  36. const timeMatch = data.publish_time_pt?.match(/^(1[0-2]|[1-9]):([0-5][0-9])(am|pm)$/i);
  37. if (!timeMatch) continue;
  38. let hours = parseInt(timeMatch[1]);
  39. const mins = parseInt(timeMatch[2]);
  40. const isPm = timeMatch[3].toLowerCase() === 'pm';
  41. if (hours === 12) hours = isPm ? 12 : 0;
  42. else if (isPm) hours += 12;
  43. const postMinutes = hours * 60 + mins;
  44. // Check if post is past publish date/time
  45. const isPublished = nowDate > data.publish_date ||
  46. (nowDate === data.publish_date && nowMinutes >= postMinutes);
  47. if (isPublished && data.slug) {
  48. posts.push(data.slug);
  49. }
  50. }
  51. return posts;
  52. }
  53. /** @type {import('next-sitemap').IConfig} */
  54. module.exports = {
  55. siteUrl: process.env.NEXT_PUBLIC_SITE_URL || 'https://roocode.com',
  56. generateRobotsTxt: true,
  57. generateIndexSitemap: false, // We don't need index sitemap for a small site
  58. changefreq: 'monthly',
  59. priority: 0.7,
  60. sitemapSize: 5000,
  61. exclude: [
  62. '/api/*',
  63. '/server-sitemap-index.xml',
  64. '/404',
  65. '/500',
  66. '/_not-found',
  67. ],
  68. robotsTxtOptions: {
  69. policies: [
  70. {
  71. userAgent: '*',
  72. allow: '/',
  73. },
  74. ],
  75. additionalSitemaps: [
  76. // Add any additional sitemaps here if needed in the future
  77. ],
  78. },
  79. // Custom transform function to set specific priorities and change frequencies
  80. transform: async (config, path) => {
  81. // Set custom priority for specific pages
  82. let priority = config.priority;
  83. let changefreq = config.changefreq;
  84. if (path === '/') {
  85. priority = 1.0;
  86. changefreq = 'yearly';
  87. } else if (path === '/enterprise' || path === '/evals') {
  88. priority = 0.8;
  89. changefreq = 'monthly';
  90. } else if (path === '/privacy' || path === '/terms') {
  91. priority = 0.5;
  92. changefreq = 'yearly';
  93. } else if (path === '/blog') {
  94. priority = 0.8;
  95. changefreq = 'weekly';
  96. } else if (path.startsWith('/blog/')) {
  97. priority = 0.7;
  98. changefreq = 'monthly';
  99. }
  100. return {
  101. loc: path,
  102. changefreq,
  103. priority,
  104. lastmod: config.autoLastmod ? new Date().toISOString() : undefined,
  105. alternateRefs: config.alternateRefs ?? [],
  106. };
  107. },
  108. additionalPaths: async (config) => {
  109. const result = [];
  110. // Add the /evals page since it's a dynamic route
  111. result.push({
  112. loc: '/evals',
  113. changefreq: 'monthly',
  114. priority: 0.8,
  115. lastmod: new Date().toISOString(),
  116. });
  117. // Add /blog index
  118. result.push({
  119. loc: '/blog',
  120. changefreq: 'weekly',
  121. priority: 0.8,
  122. lastmod: new Date().toISOString(),
  123. });
  124. // Add published blog posts
  125. try {
  126. const slugs = getPublishedBlogPosts();
  127. for (const slug of slugs) {
  128. result.push({
  129. loc: `/blog/${slug}`,
  130. changefreq: 'monthly',
  131. priority: 0.7,
  132. lastmod: new Date().toISOString(),
  133. });
  134. }
  135. } catch (e) {
  136. console.warn('Could not load blog posts for sitemap:', e.message);
  137. }
  138. return result;
  139. },
  140. };