fix(updater): convert HTML release notes to markdown before rendering
electron-updater returns GitHub release bodies as HTML, but the update dialog renders content with ReactMarkdown which expects markdown input. This caused raw HTML tags to display as visible text in the update notification. Convert HTML to markdown in formatReleaseNotes() so the renderer's existing markdown pipeline works correctly. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -45,6 +45,66 @@ type UpdateChannel = 'latest' | 'beta';
|
||||
// Store interval ID for cleanup during shutdown
|
||||
let periodicCheckIntervalId: ReturnType<typeof setInterval> | null = null;
|
||||
|
||||
/**
|
||||
* Convert basic HTML (from GitHub release bodies) to markdown.
|
||||
* Handles the common tags GitHub uses in release notes.
|
||||
*/
|
||||
function htmlToMarkdown(html: string): string {
|
||||
let md = html;
|
||||
|
||||
// Block-level replacements
|
||||
md = md.replace(/<h1[^>]*>(.*?)<\/h1>/gi, '# $1\n\n');
|
||||
md = md.replace(/<h2[^>]*>(.*?)<\/h2>/gi, '## $1\n\n');
|
||||
md = md.replace(/<h3[^>]*>(.*?)<\/h3>/gi, '### $1\n\n');
|
||||
md = md.replace(/<h4[^>]*>(.*?)<\/h4>/gi, '#### $1\n\n');
|
||||
|
||||
// Lists: convert <ol>/<ul> with <li> items
|
||||
// First handle <li> within <ol> (numbered)
|
||||
md = md.replace(/<ol[^>]*>([\s\S]*?)<\/ol>/gi, (_match, content: string) => {
|
||||
let i = 0;
|
||||
return content.replace(/<li[^>]*>([\s\S]*?)<\/li>/gi, (_m: string, text: string) => {
|
||||
i++;
|
||||
return `${i}. ${text.trim()}\n`;
|
||||
}) + '\n';
|
||||
});
|
||||
// Then <li> within <ul> (bulleted)
|
||||
md = md.replace(/<ul[^>]*>([\s\S]*?)<\/ul>/gi, (_match, content: string) => {
|
||||
return content.replace(/<li[^>]*>([\s\S]*?)<\/li>/gi, (_m: string, text: string) => {
|
||||
return `- ${text.trim()}\n`;
|
||||
}) + '\n';
|
||||
});
|
||||
|
||||
// Inline replacements
|
||||
md = md.replace(/<strong[^>]*>(.*?)<\/strong>/gi, '**$1**');
|
||||
md = md.replace(/<b[^>]*>(.*?)<\/b>/gi, '**$1**');
|
||||
md = md.replace(/<em[^>]*>(.*?)<\/em>/gi, '*$1*');
|
||||
md = md.replace(/<i[^>]*>(.*?)<\/i>/gi, '*$1*');
|
||||
md = md.replace(/<code[^>]*>(.*?)<\/code>/gi, '`$1`');
|
||||
md = md.replace(/<tt[^>]*>(.*?)<\/tt>/gi, '`$1`');
|
||||
md = md.replace(/<a[^>]*href="([^"]*)"[^>]*>(.*?)<\/a>/gi, '[$2]($1)');
|
||||
|
||||
// Block elements
|
||||
md = md.replace(/<p[^>]*>([\s\S]*?)<\/p>/gi, '$1\n\n');
|
||||
md = md.replace(/<br\s*\/?>/gi, '\n');
|
||||
md = md.replace(/<hr\s*\/?>/gi, '---\n\n');
|
||||
|
||||
// Remove any remaining HTML tags
|
||||
md = md.replace(/<[^>]+>/g, '');
|
||||
|
||||
// Decode common HTML entities
|
||||
md = md.replace(/&/g, '&');
|
||||
md = md.replace(/</g, '<');
|
||||
md = md.replace(/>/g, '>');
|
||||
md = md.replace(/"/g, '"');
|
||||
md = md.replace(/'/g, "'");
|
||||
md = md.replace(/ /g, ' ');
|
||||
|
||||
// Clean up excessive whitespace
|
||||
md = md.replace(/\n{3,}/g, '\n\n');
|
||||
|
||||
return md.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert releaseNotes from electron-updater to a markdown string.
|
||||
* releaseNotes can be:
|
||||
@@ -57,8 +117,12 @@ function formatReleaseNotes(releaseNotes: UpdateInfo['releaseNotes']): string |
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// If it's already a string, return as-is
|
||||
// If it's a string, convert HTML to markdown if needed
|
||||
// electron-updater returns GitHub release bodies as HTML
|
||||
if (typeof releaseNotes === 'string') {
|
||||
if (releaseNotes.trimStart().startsWith('<')) {
|
||||
return htmlToMarkdown(releaseNotes);
|
||||
}
|
||||
return releaseNotes;
|
||||
}
|
||||
|
||||
@@ -74,8 +138,12 @@ function formatReleaseNotes(releaseNotes: UpdateInfo['releaseNotes']): string |
|
||||
.filter(item => item.note) // Filter out entries with null/undefined notes
|
||||
.map(item => {
|
||||
// Each item has version and note properties
|
||||
// note can be HTML (GitHub provider) so convert if needed
|
||||
const versionHeader = item.version ? `## ${item.version}\n` : '';
|
||||
return `${versionHeader}${item.note}`;
|
||||
const note = typeof item.note === 'string' && item.note.trimStart().startsWith('<')
|
||||
? htmlToMarkdown(item.note)
|
||||
: item.note;
|
||||
return `${versionHeader}${note}`;
|
||||
})
|
||||
.join('\n\n');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user