Skip to content

Conversation

@gaspergrom
Copy link
Collaborator

No description provided.

Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
@gaspergrom gaspergrom self-assigned this Jan 21, 2026
Copilot AI review requested due to automatic review settings January 21, 2026 08:01
@gaspergrom gaspergrom merged commit 87c11a4 into main Jan 21, 2026
12 checks passed
@gaspergrom gaspergrom deleted the feat/docs-blog-sitemap branch January 21, 2026 08:01
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR modernizes sitemap generation by replacing static hardcoded paths with dynamic content retrieval. For leaderboards, it now fetches active leaderboard types from Tinybird instead of using a static config file. For docs and blog sections, it dynamically scans the filesystem to discover markdown files with index.md, eliminating the need to manually update sitemap entries when content changes.

Changes:

  • Replaced static leaderboard config import with dynamic Tinybird API call
  • Replaced static docs links with filesystem scanning of the docs directory structure
  • Replaced static blog links with filesystem scanning of the blog directory structure

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 9 comments.

File Description
frontend/server/api/seo/sitemap/leaderboards.ts Fetches leaderboard types dynamically from Tinybird API instead of using static config
frontend/server/api/seo/sitemap/docs.ts Recursively scans docs directory to find all folders with index.md files
frontend/server/api/seo/sitemap/blog.ts Scans blog directory to find all blog post folders with index.md files

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

} catch {
// No index.md, continue recursively
}
paths.push(...getMarkdownPaths(fullPath, basePath));
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The recursive directory traversal in getMarkdownPaths doesn't check for symbolic links, which could cause infinite loops if there are circular symlinks in the docs directory. Consider checking if entries are symbolic links before recursing, or using a safeguard like tracking visited directories.

Copilot uses AI. Check for mistakes.
Comment on lines +23 to +30
} catch {
// No index.md, continue recursively
}
paths.push(...getMarkdownPaths(fullPath, basePath));
}
}
} catch {
// Directory doesn't exist or can't be read
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error handling in the getMarkdownPaths function silently swallows all errors with empty catch blocks. While this prevents the sitemap generation from failing, it makes debugging difficult. Consider logging errors to help identify issues with file system access or permissions.

Suggested change
} catch {
// No index.md, continue recursively
}
paths.push(...getMarkdownPaths(fullPath, basePath));
}
}
} catch {
// Directory doesn't exist or can't be read
} catch (error) {
// No index.md, continue recursively; log in case this is an unexpected filesystem error
console.error(`Error checking index.md at path "${indexPath}":`, error);
}
paths.push(...getMarkdownPaths(fullPath, basePath));
}
}
} catch (error) {
// Directory doesn't exist or can't be read; log for debugging
console.error(`Error reading documentation directory "${dir}":`, error);

Copilot uses AI. Check for mistakes.
Comment on lines +27 to +28
} catch {
// Directory doesn't exist or can't be read
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error handling in the getBlogPosts function silently swallows all errors with empty catch blocks. While this prevents the sitemap generation from failing, it makes debugging difficult. Consider logging errors to help identify issues with file system access or permissions.

Suggested change
} catch {
// Directory doesn't exist or can't be read
} catch (error) {
// Directory doesn't exist or can't be read
console.error('Failed to read blog directory "%s":', dir, error);

Copilot uses AI. Check for mistakes.
Comment on lines +22 to +28
} catch {
// No index.md, skip
}
}
}
} catch {
// Directory doesn't exist or can't be read
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error handling at line 22 also silently swallows errors with an empty catch block. Consider logging errors to help identify issues with file system access or permissions.

Suggested change
} catch {
// No index.md, skip
}
}
}
} catch {
// Directory doesn't exist or can't be read
} catch (error) {
// No index.md, skip
console.error(`Error accessing blog post index at "${indexPath}":`, error);
}
}
}
} catch (error) {
// Directory doesn't exist or can't be read
console.error(`Error reading blog directory "${dir}":`, error);

Copilot uses AI. Check for mistakes.
import { readdirSync, statSync } from 'fs';
import { join } from 'path';

function getBlogPosts(dir: string): string[] {
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function name getBlogPosts doesn't accurately describe what it does. The function returns paths (strings starting with '/'), not blog post objects. Consider renaming to getBlogPostPaths for clarity.

Copilot uses AI. Check for mistakes.
];
import { readdirSync, statSync } from 'fs';
import { join, relative } from 'path';

Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getMarkdownPaths function lacks documentation. Consider adding a JSDoc comment explaining the function's purpose, parameters, and return value, especially since it has somewhat complex recursive logic.

Suggested change
/**
* Recursively collects relative paths for documentation sections that contain an `index.md` file.
*
* Starting from the given directory, this walks all non-hidden subdirectories. For each directory
* that contains an `index.md` file, it adds a normalized path (starting and ending with `/`)
* relative to the provided base path. Subdirectories are always traversed, even if they do not
* contain an `index.md`.
*
* @param dir - Absolute or base directory path from which to start scanning for markdown indexes.
* @param basePath - Base path used as the root when computing relative paths with `path.relative`.
* This should typically be the top-level docs directory.
* @returns An array of relative directory paths (e.g. `/guide/`, `/reference/api/`) that contain
* an `index.md` file.
*/

Copilot uses AI. Check for mistakes.
Comment on lines +7 to 13
const res = await fetchFromTinybird<{ leaderboardType: string }[]>(
'/v0/pipes/sitemap_leaderboards.json',
{},
);
return res.data.map((item) => ({
loc: `/leaderboards/${item.leaderboardType}`,
}));
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fetchFromTinybird call doesn't include proper error handling. If the Tinybird API call fails or returns an unexpected response, this will cause the sitemap generation to fail entirely. Consider adding try-catch error handling to gracefully handle API failures and return an empty array or a fallback response.

Suggested change
const res = await fetchFromTinybird<{ leaderboardType: string }[]>(
'/v0/pipes/sitemap_leaderboards.json',
{},
);
return res.data.map((item) => ({
loc: `/leaderboards/${item.leaderboardType}`,
}));
try {
const res = await fetchFromTinybird<{ leaderboardType: string }[]>(
'/v0/pipes/sitemap_leaderboards.json',
{},
);
if (!res || !Array.isArray(res.data)) {
return [];
}
return res.data.map((item) => ({
loc: `/leaderboards/${item.leaderboardType}`,
}));
} catch (error) {
// Gracefully handle Tinybird errors by returning an empty sitemap segment
return [];
}

Copilot uses AI. Check for mistakes.
Comment on lines +23 to +30
} catch {
// No index.md, continue recursively
}
paths.push(...getMarkdownPaths(fullPath, basePath));
}
}
} catch {
// Directory doesn't exist or can't be read
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error handling at line 23 also silently swallows errors with an empty catch block. Consider logging errors to help identify issues with file system access or permissions.

Suggested change
} catch {
// No index.md, continue recursively
}
paths.push(...getMarkdownPaths(fullPath, basePath));
}
}
} catch {
// Directory doesn't exist or can't be read
} catch (error) {
// No index.md, or index file is not accessible; continue recursively
console.error(`Failed to access index.md at path "${indexPath}":`, error);
}
paths.push(...getMarkdownPaths(fullPath, basePath));
}
}
} catch (error) {
// Directory doesn't exist or can't be read
console.error(`Failed to read directory "${dir}":`, error);

Copilot uses AI. Check for mistakes.
const staticLinks = ['/introducing-insights/', '/first-3-months/', '/product-update-nov-2025/'];
import { readdirSync, statSync } from 'fs';
import { join } from 'path';

Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getBlogPosts function lacks documentation. Consider adding a JSDoc comment explaining the function's purpose, parameters, and return value.

Suggested change
/**
* Reads the given directory and returns URL path segments for blog posts.
* A blog post is identified as a subdirectory (excluding dot-prefixed directories)
* that contains an `index.md` file.
*
* @param dir - Absolute or relative path to the root blog directory to scan.
* @returns An array of blog path segments (e.g. `"/post-slug/"`) for use in the sitemap.
*/

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants