npm.io
4.1.0 • Published 1 month agoCLI

changelog-parser

Licence
ISC
Version
4.1.0
Deps
1
Size
21 kB
Vulns
0
Weekly
0
Stars
82

changelog-parser

Change log parser for node.

npm build downloads

Features

  • Parses a CHANGELOG.md file (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-DD headings, ### 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 CHANGES section
  • 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-parser

Usage

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 globally

This installs a program called changelog-parser that you simply pass a CHANGELOG.md file.

changelog-parser path/to/CHANGELOG.md

This 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 named import { parseChangelog }.
  • CommonJS: a bare require('changelog-parser') no longer returns the function. On Node 22.12+, where require() of an ES module is supported, use const { parseChangelog } = require('changelog-parser'). On older Node, use a dynamic import(). Node 22.12+ is only needed for this path.
  • body and description are now always joined with \n rather 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

ISC

Log image is from emojipedia.

Keywords