npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

patchnotes

v0.1.0

Published

Turn git log ranges into readable, grouped patch notes. Zero runtime dependencies, browser-safe core.

Downloads

172

Readme

patchnotes

npm version CI license

Turn a range of git commits into patch notes a human can read.

npx patchnotes

That is the whole setup. Inside a git repository, with no arguments, patchnotes takes every commit since your latest tag and prints grouped markdown. No config file, no runtime dependencies.

  • Zero config — sensible defaults for the common case, flags only when you want them.
  • Zero runtime dependencies — nothing is installed but the tool itself.
  • Honest — it cleans up commit messages, it never invents or inflates content. A commit that says nothing useful is dropped instead of padded out.
  • Browser-safe core — the parser and formatters are a pure library with no Node built-ins, so the same code runs in a web page.

Example

Input — the commits in the range:

a1b2c3d feat(cli): read git log from stdin
b2c3d4e fix: stop crashing on an empty range
c3d4e5f feat!: drop node 16
d4e5f6a Merge pull request #7 from Chu13/stdin
e5f6a7b chore: bump dev dependencies
f6a7b8c tidied up a few stray files

Output — patchnotes --title v1.1.0:

## v1.1.0

_2026-07-24_

### 💥 Breaking

- Drop node 16

### ✨ Features

- **cli:** Read git log from stdin

### 🐛 Fixes

- Stop crashing on an empty range

### 📦 Changes

- Tidied up a few stray files

### 🔧 Internal

- Bump dev dependencies

The merge commit is gone, the breaking change is at the top, the commit that did not follow any convention still made it in under Changes instead of breaking the run.

Usage

# Everything since the latest tag (the default)
npx patchnotes

# An explicit range
npx patchnotes v1.2.0..HEAD

# By date instead of by range
npx patchnotes --since "2 weeks ago"

# Only the things users care about
npx patchnotes --only feat,fix

# Feed it text instead — works outside a repository
git log v1.0.0..HEAD | npx patchnotes

# Write a changelog entry
npx patchnotes --title v1.1.0 >> CHANGELOG.md

Install it locally if you would rather not go through npx:

npm install --save-dev patchnotes

Options

| Option | Default | Description | | --- | --- | --- | | [range] | <latest tag>..HEAD | Positional revision range, e.g. v1.2.0..HEAD. Every commit when the repository has no tags. | | --since <date> | — | Only commits more recent than this git date, e.g. "2 weeks ago". | | -f, --format <format> | md | md, json or text. | | -t, --title <title> | the range, or the target tag | Heading of the output. | | -d, --date <date> | today, YYYY-MM-DD | Date stamp. | | --only <types> | — | Comma separated commit types to keep, e.g. feat,fix. Non-conventional commits are excluded when this is used. | | --no-emoji | — | Plain markdown headings. | | --include-merges | — | Keep merge commits, which are dropped by default. | | -h, --help | — | Show usage. | | -v, --version | — | Show the version. |

patchnotes reads git log text from stdin whenever something is piped in, which is what makes the browser playground and the CLI share a single parser.

How commits are grouped

| Commit | Kind | Section | | --- | --- | --- | | type!: or a BREAKING CHANGE: footer | breaking | 💥 Breaking | | feat: | feature | ✨ Features | | fix: | fix | 🐛 Fixes | | chore: refactor: perf: build: ci: test: docs: style: revert: | internal | 🔧 Internal | | Anything else, including non-conventional subjects | change | 📦 Changes |

Sections are always printed in that order, and commits keep their relative order inside a section.

A few deliberate decisions:

  • Merge commits are dropped. Merge branch ... and Merge pull request ... carry no information the underlying commits do not already carry. Use --include-merges if you disagree.
  • Reverts are labelled. Revert "feat: add json output" becomes Revert: Add json output under Internal.
  • A BREAKING CHANGE: footer wins. When a commit body explains the break, that explanation is used as the text instead of the subject line.
  • Text is tidied, not rewritten. Whitespace is collapsed, a trailing period is dropped and the first letter is capitalised — unless the first word looks like code (parseGitLog, tsup.config.ts), which is left alone.

JSON output

--format json emits exactly this shape:

{
  "date": "2026-07-24",
  "title": "v1.1.0",
  "items": [
    { "kind": "breaking", "text": "Drop node 16" },
    { "kind": "feature", "text": "Read git log from stdin", "scope": "cli" },
    { "kind": "fix", "text": "Stop crashing on an empty range" },
    { "kind": "change", "text": "Tidied up a few stray files" },
    { "kind": "internal", "text": "Bump dev dependencies" }
  ]
}
  • dateYYYY-MM-DD, taken from --date or today.
  • title — from --title, otherwise the range or the target tag.
  • items[].kind — one of breaking, feature, fix, change, internal.
  • items[].text — the cleaned up message. Never empty.
  • items[].scope — present only when the commit had a conventional scope.

Use it as a library

The core is a pure ES module: no dependencies, no node: imports, no clock and no environment access. It runs unchanged in a browser.

import { patchnotesFromLog, formatMarkdown } from "patchnotes";

const notes = patchnotesFromLog(gitLogText, {
  date: "2026-07-24",
  title: "v1.1.0",
});

console.log(formatMarkdown(notes));
console.log(notes.items); // [{ kind: "feature", text: "..." }, ...]

In a page, straight from a CDN:

<script type="module">
  import { patchnotesFromLog, formatMarkdown } from "https://esm.sh/patchnotes";

  const notes = patchnotesFromLog(document.querySelector("textarea").value, {
    date: new Date().toISOString().slice(0, 10),
    title: "Patch Notes",
  });

  document.querySelector("pre").textContent = formatMarkdown(notes);
</script>

parseGitLog accepts the default git log output and --oneline output, detected automatically, so you can hand it whatever a visitor pastes.

API

| Export | Description | | --- | --- | | patchnotesFromLog(log, options?) | git log text in, PatchNotes out. | | parseGitLog(text) | Text to ParsedCommit[]. Default and --oneline formats. | | parseGitLogRecords(text) | The %H%x1f%s%x1f%b%x1e record format the CLI uses. | | parseCommit(hash, subject, body) | Analyse a single commit message. | | buildNotes(commits, options?) | ParsedCommit[] to PatchNotes. | | formatMarkdown / formatJson / formatText | Render PatchNotes. | | formatPatchNotes(notes, format, options?) | Render by format name. | | cleanText(text) | The message tidying rule on its own. | | KIND_ORDER | The section order. |

buildNotes never reads the clock: date defaults to "" and the caller supplies it. That is what makes the core deterministic and trivially testable — the CLI is the only part that knows what day it is.

A runnable example lives in examples/browser.html: paste git log text into a textarea, see the patch notes update as you type. Build first, then serve the folder:

npm run build
npx serve .   # then open /examples/browser.html

How jabordones.com uses this

www.jabordones.com has a /log page — patch notes for the site itself. Those entries used to be written by hand in src/data/patchnotes.ts. Now they come out of this tool:

npx patchnotes --format json --title "v1.4.0"

The JSON shape above is the entry type that page renders, so the output drops straight into the data file. The same core also powers the interactive playground on the site's /lab page, which is the browser example in this repository with a nicer coat of paint.

The CHANGELOG.md of this repository is generated by the tool itself, and every GitHub release body is written by node dist/cli.js inside the release workflow. If the output ever stops being readable, it shows up here first.

Development

npm install
npm run typecheck     # tsc --noEmit
npm test              # vitest run
npm run build         # tsup, dist/index.js + dist/cli.js
npm run check:browser # fails if the core ever imports a node built-in

check:browser bundles src/core/index.ts with esbuild for the browser platform. It is the guard that keeps the library half of this package honest, and it runs in CI.

Resumen en español

patchnotes convierte un rango de commits de git en notas de versión legibles.

npx patchnotes

Sin argumentos y dentro de un repositorio, toma todos los commits desde la última etiqueta y imprime markdown agrupado: 💥 Breaking, ✨ Features, 🐛 Fixes, 📦 Changes y 🔧 Internal. Sin configuración y sin dependencias en tiempo de ejecución.

  • Entiende conventional commits (feat:, fix(scope):, feat!:, footers BREAKING CHANGE:) y no se rompe con los que no siguen ninguna convención: acaban en Changes.
  • Los merges se descartan, los reverts se etiquetan y los mensajes se limpian, pero nunca se inventa contenido. Si un commit no dice nada útil, se descarta.
  • Tres formatos: --format md (por defecto), --format json y --format text. El JSON usa la forma { date, title, items: [{ kind, text }] }.
  • También lee de stdin (git log v1.0.0..HEAD | npx patchnotes), así que funciona fuera de un repositorio.
  • El core es una librería pura sin dependencias ni módulos de Node, importable desde el navegador. Hay un ejemplo funcionando en examples/browser.html.

Opciones: --since, --format, --title, --date, --only feat,fix, --no-emoji, --include-merges, --help, --version.

License

MIT © Jesus Bordones