changelog-parser
v4.1.0
Published
Change log parser for node.
Downloads
129,405
Readme
changelog-parser
Change log parser for node.
Features
- Parses a
CHANGELOG.mdfile (or raw text string) into a structured object: a title, an optional description, and an array of version entries. - Extracts each version's number, date, body, and list items, grouped by
### subheading. - Promise-based, with a legacy Node-style callback also supported.
- Optionally strips markdown from parsed entries.
Supported formats
These common changelog formats are officially supported:
- Keep a Changelog:
## [x.y.z] - YYYY-MM-DDheadings,### Added/### Changed/ etc. sections - Conventional Changelog:
## [x.y.z](compare) (date)headings, with or without top-level title - Release Please:
## [x.y.z](compare) (date)headings with### ⚠ BREAKING CHANGESsection - Standard Version: headings set by release type (
#major,##minor,###patch) - auto-changelog:
#### [vx.y.z](compare)headings with the date on a> ...blockquote line
Maintained with 100% test coverage, verified on Linux, macOS, and Windows across all supported Node.js versions.
Install
npm install changelog-parserUsage
This module is ESM-only. It exports a named parseChangelog function that parses a changelog and returns a Promise.
import { parseChangelog } from 'changelog-parser'
// async / await
const changelog = await parseChangelog('path/to/CHANGELOG.md')
// or as a promise
parseChangelog('path/to/CHANGELOG.md')
.then((changelog) => console.log(changelog))
.catch((err) => console.error(err))A default export of the same function is also available (import parseChangelog from 'changelog-parser').
CommonJS consumers on Node.js >= 22.12 can still require() it, but the function is no longer the module itself:
const { parseChangelog } = require('changelog-parser')Callback
A Node-style callback is also supported. It was the original interface (the package predates native promises) and is kept for backwards compatibility. New code should use the returned promise.
parseChangelog('path/to/CHANGELOG.md', (err, result) => {
if (err) throw err
console.log(result)
})Options
The first argument can be a path string or an options object (string | ChangelogOptions). You must provide either filePath or text.
interface ChangelogOptions {
/** Path to a changelog file. */
filePath?: string
/** Raw changelog text (alternative to `filePath`). */
text?: string
/** Strip markdown from parsed entries. Defaults to `true`. */
removeMarkdown?: boolean
}Command-line interface
There is also a command-line interface available.
npx changelog-parser <path-to-changelog.md> # invoke with npx
npm install -g changelog-parser # or install globallyThis installs a program called changelog-parser that you simply pass a CHANGELOG.md file.
changelog-parser path/to/CHANGELOG.mdThis will print the JSON object representing the change log to the terminal.
Alternately you can run it without arguments and it will look for a CHANGELOG.md file in the working directory.
Migrating from 3.x
As of v4.0.0 this package is ESM-only. The API and output are otherwise unchanged.
- ESM:
import parseChangelog from 'changelog-parser', or the namedimport { parseChangelog }. - CommonJS: a bare
require('changelog-parser')no longer returns the function. On Node 22.12+, whererequire()of an ES module is supported, useconst { parseChangelog } = require('changelog-parser'). On older Node, use a dynamicimport(). Node 22.12+ is only needed for this path. bodyanddescriptionare now always joined with\nrather than the OS line ending, so output no longer varies by platform.
Output
parseChangelog resolves to (and the callback receives) a Changelog. These types are exported.
interface Changelog {
/** The document title (the first `#` heading that is not itself a version), if present. */
title?: string
/** Free text between the title and the first version, if any. */
description?: string
/** Version entries, in document order. */
versions: ChangelogVersion[]
}
interface ChangelogVersion {
/** Semver string if the heading is semver-compliant, otherwise `null`. */
version: string | null
/** The raw heading text. */
title: string | null
/** Date parsed from the heading if present, otherwise `null`. */
date: string | null
/** All content under the heading, as a single string. */
body: string
/**
* List items grouped by subheading. The `_` key holds every list item;
* each `### Subheading` adds a key with the items beneath it.
*/
parsed: Record<string, string[]>
}For example, this changelog:
# Changelog
## [1.1.0] - 2024-03-15
### Added
* A `format` option.
### Fixed
* Squashed a bug.
## [1.0.0] - 2024-01-01
* First release.parses to:
{
title: 'Changelog',
versions: [
{
version: '1.1.0',
title: '[1.1.0] - 2024-03-15',
date: '2024-03-15',
body: '### Added\n\n* A `format` option.\n\n### Fixed\n\n* Squashed a bug.',
parsed: {
_: ['A format option.', 'Squashed a bug.'],
Added: ['A format option.'],
Fixed: ['Squashed a bug.']
}
},
{
version: '1.0.0',
title: '[1.0.0] - 2024-01-01',
date: '2024-01-01',
body: '* First release.',
parsed: { _: ['First release.'] }
}
]
}A version heading can be at any level (# through ######). A # or ## is always a version, except a # that is the document title (the first # heading that is not itself a version). At ### and deeper, a heading is a version only when it looks like one, so conventional-changelog / standard-version patch headings (###) and auto-changelog version headings (####) are captured, while a ### Section such as ### Added is not.
The version number is read whether the heading is bare (2.1.0), v-prefixed (v1.0.0), or wrapped in brackets and optionally linked ([0.9.0](...)). It is null when the heading is not semver-compliant (such as Unreleased). Dates are recognized in common numeric formats (YYYY-MM-DD, DD.MM.YYYY, parenthesized (YYYY-MM-DD)) and textual English formats (20 January 2021, January 20, 2021). The date is taken from the heading, or from a > blockquote line directly beneath it when the heading carries none (as auto-changelog writes them).
parsed is flat: it captures one entry per top-level list item. Nested or indented sub-items are not split into their own entries, but the full original text (including any nesting) is always available in body.
Contributing
Contributions welcome! Please read the contributing guidelines first.
License
Log image is from emojipedia.
