From ae6d1e638d0f7bf29f5b4373ac095e7e14ff0db8 Mon Sep 17 00:00:00 2001 From: Sahil Aujla Date: Thu, 19 Mar 2026 14:04:51 -0400 Subject: [PATCH 1/3] feat: generate unique meta descriptions for changelog entries Modify the changelog indexer to auto-generate a description for each entry based on its H2 headings. Produces descriptions like "Week of January 8, 2026: updates to Developer Experience and Node." which differentiates changelog pages in search results. Adds an optional description field to ChangelogPathIndexEntry and includes it in both the path index and Algolia records. --- src/content-indexer/indexers/changelog.ts | 57 +++++++++++++++++++++++ src/content-indexer/types/pathIndex.ts | 1 + 2 files changed, 58 insertions(+) diff --git a/src/content-indexer/indexers/changelog.ts b/src/content-indexer/indexers/changelog.ts index 2873347bd..4fae9cbfc 100644 --- a/src/content-indexer/indexers/changelog.ts +++ b/src/content-indexer/indexers/changelog.ts @@ -16,6 +16,52 @@ export interface ChangelogIndexerConfig { branchId: string; } +const MONTH_NAMES = [ + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December", +]; + +/** + * Extract H2 headings from markdown content and build a description string. + * Example output: "Week of January 8, 2026: updates to Developer Experience and Node." + */ +const buildChangelogDescription = ( + content: string, + date: string, + month: string, + day: string, + year: string, +): string => { + const headings = Array.from(content.matchAll(/^## (.+)$/gm)).map( + (m) => m[1].trim(), + ); + + const monthName = MONTH_NAMES[Number(month) - 1]; + const dayNum = Number(day); + const prefix = `Week of ${monthName} ${dayNum}, ${year}`; + + if (headings.length === 0) { + return `${prefix}: changelog updates.`; + } + + const joined = + headings.length === 1 + ? headings[0] + : `${headings.slice(0, -1).join(", ")} and ${headings[headings.length - 1]}`; + + return `${prefix}: updates to ${joined}.`; +}; + /** * Parse a changelog filename (e.g., "2025-11-20.md") into date components */ @@ -89,11 +135,21 @@ export const buildChangelogIndex = async ( const route = `${year}/${Number(month)}/${Number(day)}`; const fullPath = `changelog/${route}`; + // Generate a unique description from H2 headings + const description = buildChangelogDescription( + content, + date, + month, + day, + year, + ); + // Create path index entry const pathIndexEntry: ChangelogPathIndexEntry = { type: "changelog", date, // ISO date string like "2025-12-11" filePath: filename, // Filename like "2025-12-11.md" + description, }; // Generate hash-based objectID from path (consistent with docs/SDK) @@ -107,6 +163,7 @@ export const buildChangelogIndex = async ( objectID, indexerType: "changelog", title: `Changelog - ${date}`, + description, content, // Raw markdown - truncateRecord will clean it path: fullPath, pageType: "Changelog" as const, diff --git a/src/content-indexer/types/pathIndex.ts b/src/content-indexer/types/pathIndex.ts index 9abccfc28..302fbc9c2 100644 --- a/src/content-indexer/types/pathIndex.ts +++ b/src/content-indexer/types/pathIndex.ts @@ -25,6 +25,7 @@ export interface ChangelogPathIndexEntry { type: "changelog"; date: string; // ISO date string like "2025-12-11" filePath: string; // Filename like "2025-12-11.md" + description?: string; // Auto-generated from H2 headings } export type PathIndexEntry = From 5609d9aeaeef87ccc57bed41a4504e6168189f0f Mon Sep 17 00:00:00 2001 From: Sahil Aujla Date: Thu, 19 Mar 2026 14:17:04 -0400 Subject: [PATCH 2/3] docs: update preview-changelog comment to note description staleness --- src/content-indexer/uploaders/preview-changelog.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/content-indexer/uploaders/preview-changelog.ts b/src/content-indexer/uploaders/preview-changelog.ts index 9b2317f35..d1f1c42e8 100644 --- a/src/content-indexer/uploaders/preview-changelog.ts +++ b/src/content-indexer/uploaders/preview-changelog.ts @@ -47,7 +47,8 @@ export const uploadChangelogFile = async ( console.info(` 📄 ${filename} -> ${redisKey}`); // New files need reindex (to add the route to the index); content-only edits don't - // because the changelog index only stores date + filePath, not content. + // trigger a reindex. Note: the index also stores a description derived from H2 headings, + // so heading changes in preview will leave the description stale until a full reindex. return { reindexNeeded: isNew }; }; From 935f050278191e97567f2f069780da2044aa4afb Mon Sep 17 00:00:00 2001 From: Sahil Aujla Date: Fri, 20 Mar 2026 14:10:08 -0400 Subject: [PATCH 3/3] refactor: use toLocaleDateString, remove unused param, cap headings at 3 Address review feedback: - Replace MONTH_NAMES array with native toLocaleDateString - Remove unused date param from buildChangelogDescription - Cap headings at 3 to stay well within the 155 char description limit --- src/content-indexer/indexers/changelog.ts | 50 +++++++++-------------- 1 file changed, 20 insertions(+), 30 deletions(-) diff --git a/src/content-indexer/indexers/changelog.ts b/src/content-indexer/indexers/changelog.ts index 4fae9cbfc..3321359f2 100644 --- a/src/content-indexer/indexers/changelog.ts +++ b/src/content-indexer/indexers/changelog.ts @@ -16,39 +16,35 @@ export interface ChangelogIndexerConfig { branchId: string; } -const MONTH_NAMES = [ - "January", - "February", - "March", - "April", - "May", - "June", - "July", - "August", - "September", - "October", - "November", - "December", -]; +const MAX_HEADINGS = 3; + +/** + * Format a date string (YYYY-MM-DD) to long date format (e.g., "January 8, 2026") + */ +const formatLongDate = (dateString: string): string => { + const [year, month, day] = dateString.split("-").map(Number); + const date = new Date(year, month - 1, day); + return date.toLocaleDateString("en-US", { + year: "numeric", + month: "long", + day: "numeric", + }); +}; /** * Extract H2 headings from markdown content and build a description string. + * Caps at MAX_HEADINGS to stay within the ~155 character meta description limit. * Example output: "Week of January 8, 2026: updates to Developer Experience and Node." */ const buildChangelogDescription = ( content: string, date: string, - month: string, - day: string, - year: string, ): string => { - const headings = Array.from(content.matchAll(/^## (.+)$/gm)).map( - (m) => m[1].trim(), - ); + const headings = Array.from(content.matchAll(/^## (.+)$/gm)) + .map((m) => m[1].trim()) + .slice(0, MAX_HEADINGS); - const monthName = MONTH_NAMES[Number(month) - 1]; - const dayNum = Number(day); - const prefix = `Week of ${monthName} ${dayNum}, ${year}`; + const prefix = `Week of ${formatLongDate(date)}`; if (headings.length === 0) { return `${prefix}: changelog updates.`; @@ -136,13 +132,7 @@ export const buildChangelogIndex = async ( const fullPath = `changelog/${route}`; // Generate a unique description from H2 headings - const description = buildChangelogDescription( - content, - date, - month, - day, - year, - ); + const description = buildChangelogDescription(content, date); // Create path index entry const pathIndexEntry: ChangelogPathIndexEntry = {