(content: string)
| 154 | * @returns Record mapping version numbers to arrays of release notes |
| 155 | */ |
| 156 | export function parseChangelog(content: string): Record<string, string[]> { |
| 157 | try { |
| 158 | if (!content) return {} |
| 159 | |
| 160 | // Parse the content |
| 161 | const releaseNotes: Record<string, string[]> = {} |
| 162 | |
| 163 | // Split by heading lines (## X.X.X) |
| 164 | const sections = content.split(/^## /gm).slice(1) // Skip the first section which is the header |
| 165 | |
| 166 | for (const section of sections) { |
| 167 | const lines = section.trim().split('\n') |
| 168 | if (lines.length === 0) continue |
| 169 | |
| 170 | // Extract version from the first line |
| 171 | // Handle both "1.2.3" and "1.2.3 - YYYY-MM-DD" formats |
| 172 | const versionLine = lines[0] |
| 173 | if (!versionLine) continue |
| 174 | |
| 175 | // First part before any dash is the version |
| 176 | const version = versionLine.split(' - ')[0]?.trim() || '' |
| 177 | if (!version) continue |
| 178 | |
| 179 | // Extract bullet points |
| 180 | const notes = lines |
| 181 | .slice(1) |
| 182 | .filter(line => line.trim().startsWith('- ')) |
| 183 | .map(line => line.trim().substring(2).trim()) |
| 184 | .filter(Boolean) |
| 185 | |
| 186 | if (notes.length > 0) { |
| 187 | releaseNotes[version] = notes |
| 188 | } |
| 189 | } |
| 190 | |
| 191 | return releaseNotes |
| 192 | } catch (error) { |
| 193 | logError(toError(error)) |
| 194 | return {} |
| 195 | } |
| 196 | } |
| 197 | |
| 198 | /** |
| 199 | * Gets release notes to show based on the previously seen version. |
no test coverage detected