From 794a04484e100931e6a77f82c6ace116535f3d43 Mon Sep 17 00:00:00 2001 From: Minghao Liu <76873720+StaryMoon@users.noreply.github.com> Date: Tue, 2 Jun 2026 01:32:08 +0800 Subject: [PATCH] Fix roadmap resource title escaping (#10037) Co-authored-by: StaryMoon --- scripts/sync-content-to-repo.ts | 11 +++++++++-- src/queries/official-roadmap-topic.ts | 21 ++++++++++++++++++++- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/scripts/sync-content-to-repo.ts b/scripts/sync-content-to-repo.ts index b8fa571a6..2916681f8 100644 --- a/scripts/sync-content-to-repo.ts +++ b/scripts/sync-content-to-repo.ts @@ -3,7 +3,10 @@ import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { slugify } from '../src/lib/slugger'; import type { OfficialRoadmapDocument } from '../src/queries/official-roadmap'; -import type { OfficialRoadmapTopicContentDocument } from '../src/queries/official-roadmap-topic'; +import { + formatOfficialRoadmapTopicResourceLink, + type OfficialRoadmapTopicContentDocument, +} from '../src/queries/official-roadmap-topic'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -106,7 +109,11 @@ function prepareTopicContent(topic: OfficialRoadmapTopicContentDocument) { let content = description; if (resources.length > 0) { - content += `\n\nVisit the following resources to learn more:\n\n${resources.map((resource) => `- [@${resource.type}@${resource.title}](${resource.url})`).join('\n')}`; + const resourceLinks = resources + .map(formatOfficialRoadmapTopicResourceLink) + .join('\n'); + + content += `\n\nVisit the following resources to learn more:\n\n${resourceLinks}`; } return content; diff --git a/src/queries/official-roadmap-topic.ts b/src/queries/official-roadmap-topic.ts index 2781d7cff..2cb9079c0 100644 --- a/src/queries/official-roadmap-topic.ts +++ b/src/queries/official-roadmap-topic.ts @@ -63,6 +63,21 @@ export async function getOfficialRoadmapTopic( } } +export function escapeMarkdownLinkText(text: string) { + return text + .replace(/\\/g, '\\\\') + .replace(//g, '\\>') + .replace(/\[/g, '\\[') + .replace(/\]/g, '\\]'); +} + +export function formatOfficialRoadmapTopicResourceLink( + resource: Pick, +) { + return `- [@${resource.type}@${escapeMarkdownLinkText(resource.title)}](${resource.url})`; +} + export function prepareOfficialRoadmapTopicContent( topic: OfficialRoadmapTopicContentDocument, ) { @@ -70,7 +85,11 @@ export function prepareOfficialRoadmapTopicContent( let content = description; if (resources.length > 0) { - content += `\n\nVisit the following resources to learn more:\n\n${resources.map((resource) => `- [@${resource.type}@${resource.title}](${resource.url})`).join('\n')}`; + const resourceLinks = resources + .map(formatOfficialRoadmapTopicResourceLink) + .join('\n'); + + content += `\n\nVisit the following resources to learn more:\n\n${resourceLinks}`; } return content;