@hyperfrontend/versioning/changelog/parse

parse

Tokenizer and parser for converting Keep-a-Changelog markdown into structured Changelog objects.

tokenize walks markdown character-by-character to emit a typed Token[] stream: headings, list items, links, and section markers. parseChangelog consumes that stream and assembles entries, sections, and items in their declared order. Per-line helpers (parseVersionFromHeading, parseCommitRefs, parseIssueRefs, parseBreakingFromItem, parseScopeFromItem) extract the structured fragments embedded in entry headers and bullet items. No regex is used anywhere in the parsing pipeline, keeping behavior linear in input length and free of catastrophic backtracking risk.

parseBreakingFromItem lifts the breaking-change markers an item opens with onto the item's breaking flag, which is what keeps a parse then serialize round trip a fixpoint: the serializer re-emits exactly one marker, and any run of markers left by an earlier round trip collapses back to that one.

API Reference§

ƒ Functions

§function

parseBreakingFromItem(text: string): BreakingFromItem

Parses the breaking-change markers a changelog item can open with, lifting them out of the text and onto a flag. Serializing re-emits exactly one marker, so an item survives a parse then serialize round trip unchanged; a run of markers left behind by earlier round trips collapses back to a single flag.
Recognized openers, case-insensitive and repeatable: BREAKING, BREAKING:, [BREAKING], ⚠️ BREAKING:, a bare BREAKING:, and the BREAKING CHANGE/BREAKING CHANGES spelling of each. A bare mention needs its colon to count, so prose such as Breaking apart the parser is left alone. Scanning is character-by-character to stay ReDoS-safe.

Parameters

NameTypeDescription
§text
string
The changelog item text, without its list marker

Returns

BreakingFromItem
The breaking flag and the text that follows the markers

Example

Lifting markers out of item text

parseBreakingFromItem('**BREAKING** **BREAKING:** drop the sync open')
// => { breaking: true, text: 'drop the sync open' }

parseBreakingFromItem('add a retry budget')
// => { breaking: false, text: 'add a retry budget' }
§function

parseChangelog(content: string, source?: string): Changelog

Parses a changelog markdown string into a Changelog object.

Parameters

NameTypeDescription
§content
string
The markdown content to parse
§source?
string
Optional source file path

Returns

Changelog
Parsed Changelog object

Example

Parsing a changelog markdown string

const markdown = `# Changelog

## [1.0.0] - 2024-01-15

### Added
- Initial release`

const changelog = parseChangelog(markdown, 'CHANGELOG.md')
// => { header: { title: '# Changelog', ... }, entries: [{ version: '1.0.0', ... }] }
§function

parseCommitRefs(text: string, baseUrl?: string): CommitRef[]

Parses commit references from a line. Examples: (abc1234), [abc1234], commit abc1234

Parameters

NameTypeDescription
§text
string
The text to parse for commit references
§baseUrl?
string
Optional base URL for constructing commit links

Returns

CommitRef[]
An array of parsed CommitRef objects

Example

Parsing commit references from text

parseCommitRefs('Fixed bug (abc1234)', 'https://github.com/org/repo')
// => [{ hash: 'abc1234', shortHash: 'abc1234', url: 'https://github.com/org/repo/commit/abc1234' }]
§function

parseIssueRefs(text: string, baseUrl?: string): IssueRef[]

Parses issue/PR references from a line. Examples: #123, GH-123, closes #123

Parameters

NameTypeDescription
§text
string
The text to parse for issue references
§baseUrl?
string
Optional base URL for constructing issue links

Returns

IssueRef[]
An array of parsed IssueRef objects

Example

Parsing issue references from text

parseIssueRefs('Closes #42 and PR #123', 'https://github.com/org/repo')
// => [{ number: 42, type: 'issue', url: '...' }, { number: 123, type: 'pull-request', url: '...' }]
§function

parseScopeFromItem(text: string): ScopeFromItem

Parses the scope from a changelog item. Example: "scope: description" -> { scope: "scope", description: "description" }

Parameters

NameTypeDescription
§text
string
The text to parse for scope

Returns

ScopeFromItem
An object with optional scope and the description

Example

Parsing scope from changelog items

parseScopeFromItem('**api:** Add new endpoint')
// => { scope: 'api', description: 'Add new endpoint' }

parseScopeFromItem('Simple change without scope')
// => { scope: undefined, description: 'Simple change without scope' }
§function

parseVersionFromHeading(heading: string): ParsedVersionHeading

Parses a version string from a heading. Examples: "1.2.3", "v1.2.3", "[1.2.3]", "1.2.3 - 2024-01-01"

Parameters

NameTypeDescription
§heading
string
The heading string to parse

Returns

ParsedVersionHeading
An object containing the parsed version, date, and optional compareUrl

Example

Parsing version headings

parseVersionFromHeading('[1.2.3] - 2024-01-15')
// => { version: '1.2.3', date: '2024-01-15', compareUrl: undefined }

parseVersionFromHeading('v2.0.0')
// => { version: '2.0.0', date: null, compareUrl: undefined }
§function

tokenize(input: string): Token[]

Tokenizes a changelog markdown string into tokens.

Parameters

NameTypeDescription
§input
string
The markdown content to tokenize

Returns

Token[]
Array of tokens

Example

Tokenizing changelog markdown

const tokens = tokenize('# Changelog\n\n## [1.0.0]\n- Added feature')
// => [{ type: 'heading-1', value: 'Changelog', ... }, { type: 'heading-2', ... }, ...]

Interfaces

§interface

Token

Represents a single parsed token from the changelog markdown.

Properties

§readonly column:number
Column position where the token starts (1-indexed)
§readonly line:number
Line number where the token appears (1-indexed)
§readonly type:TokenType
The type classification of the token
§readonly value:string
The raw text content of the token

Types

§type

BreakingFromItem

Result of parseBreakingFromItem: the breaking flag plus the item text left once its leading markers are removed.
§type

TokenType

Token Types
type TokenType = "heading-1" | "heading-2" | "heading-3" | "heading-4" | "list-item" | "link-text" | "link-url" | "text" | "newline" | "blank-line" | "bold" | "code" | "eof"