@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
stringThe 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
stringThe markdown content to parse
§source?
stringOptional 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
stringThe text to parse for commit references
§baseUrl?
stringOptional 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
stringThe text to parse for issue references
§baseUrl?
stringOptional 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
stringThe 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
stringThe 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
stringThe 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 type:TokenTypeThe type classification of the token
§readonly value:stringThe raw text content of the token
§readonly line:numberLine number where the token appears (1-indexed)
§readonly column:numberColumn position where the token starts (1-indexed)

◆ 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"