Skip to content

JSLEEKR/semver-check

Repository files navigation

🔖 semver-check

Detect breaking API changes before they ship

GitHub Stars License TypeScript Tests Node


Compare TypeScript/JavaScript API surfaces across versions and flag semver violations automatically

Why This Exists | Quick Start | What It Detects | CLI Reference | Programmatic API | CI Integration


Why This Exists

Every developer has shipped a patch release that silently broke a consumer's build. A parameter became required. A type narrowed. An export vanished. The version bump said 1.0.1 but the change was a breaking 2.0.0. Users find out in their CI, not yours.

semver-check is cargo-semver-checks for the npm/TypeScript world -- it parses your public API surface, compares it against a previous version, classifies every change as breaking, feature, or fix, and tells you whether your version bump is correct before you publish.

  • 20+ detection rules -- removed exports, narrowed types, added required parameters, changed return types, and more
  • No compiler dependency -- works via regex-based analysis, so it runs in any CI environment without a full TypeScript build
  • CI-ready exit codes -- --strict mode exits non-zero on breaking changes, blocking the release pipeline automatically
  • Risk scoring -- quantifies API stability across your entire public surface, not just the changed parts
  • LCS-based diff visualization -- line-level and word-level diffs for type definition changes

Install

npm install -g semver-check

Or run without installing:

npx semver-check compare v1.0.0 v2.0.0 --strict

Requirements

  • Node.js >= 18.0.0
  • No compiler required -- pure regex-based analysis

Quick Start

# Save a snapshot of your current API
semver-check snapshot

# Make changes to your code...

# Compare against the snapshot
semver-check compare 1.0.0

# See what version you should bump to
semver-check suggest 1.0.0

# Block CI on breaking changes
semver-check compare 1.0.0 --strict

What It Detects

Breaking Changes (require major bump)

Rule Description
export-removed A previously exported symbol was removed
export-renamed An exported symbol was renamed
required-param-added A new required parameter was added to a function
param-removed A parameter was removed from a function signature
param-type-changed A parameter's type was changed
param-became-required An optional parameter became required
return-type-changed A function's return type changed
async-changed A function's async status changed
class-member-removed A public or protected class member was removed
member-visibility-changed A member's visibility was narrowed (public → protected → private)
member-type-changed A class property's type changed
type-definition-changed A type alias or interface definition changed
enum-member-removed An enum member was removed
enum-value-changed An enum member's value changed
default-export-changed The default export status of a module changed

Features (require minor bump)

Rule Description
export-added A new symbol was exported
optional-param-added A new optional parameter was added
class-member-added A new public or protected class member was added
enum-member-added A new enum member was added
deprecation-added A symbol was marked as deprecated

Fixes (require patch bump)

Rule Description
param-became-optional A required parameter became optional
deprecation-removed A deprecation marker was removed

CLI Commands

compare [base] [head]

Compare API surfaces between two versions or snapshots.

# Compare against a snapshot
semver-check compare 1.0.0

# Compare two git tags
semver-check compare v1.0.0 v2.0.0

# JSON output for CI pipelines
semver-check compare 1.0.0 --format json

# Fail (exit 1) on breaking changes
semver-check compare 1.0.0 --strict

# Markdown output for PR comments
semver-check compare 1.0.0 --format markdown
Option Description Default
--format <fmt> Output format: text, json, markdown text
--strict Exit non-zero on breaking changes false
--config <path> Path to config file auto-detect

snapshot

Save the current API surface for later comparison. Snapshots are stored in .semver-check/ and can be labeled for easy reference.

semver-check snapshot
semver-check snapshot --label pre-refactor
semver-check snapshot --label before-2.0

parse

Display the current API surface -- all exported symbols, their types, parameters, and return values.

semver-check parse
semver-check parse --format json

Example output:

Exports (12):
  function createServer(port: number, opts?: ServerOptions): Server
  function destroyServer(server: Server): Promise<void>
  class Server
    + start(): Promise<void>
    + stop(): void
    + on(event: string, handler: Function): this
  interface ServerOptions
    port?: number
    timeout?: number
  type ServerStatus = 'running' | 'stopped' | 'error'
  enum LogLevel { DEBUG, INFO, WARN, ERROR }

suggest [base]

Suggest the correct next version based on the detected changes.

semver-check suggest 1.0.0
# Suggested bump: minor -> 1.1.0

semver-check suggest 2.3.1
# Suggested bump: major -> 3.0.0 (breaking changes detected)

rules

List all 20+ available detection rules with their categories and descriptions.

semver-check rules

# List only breaking rules
semver-check rules --category breaking

# List as JSON
semver-check rules --format json

init

Scaffold a .semver-check.json configuration file in the current directory.

semver-check init

changelog [base]

Generate a changelog entry from detected API changes.

semver-check changelog 1.0.0
# Outputs a Markdown-formatted changelog block

Programmatic API

semver-check exposes a fluent builder API for integration into scripts and tools.

import { semverCheck } from 'semver-check';

const result = semverCheck()
  .fromSource(oldCode, 'index.ts', '1.0.0')
  .toSource(newCode, 'index.ts', '2.0.0')
  .format('json')
  .strict()
  .check();

console.log(result.isBreaking);        // true/false
console.log(result.suggestedVersion);  // "2.0.0"
console.log(result.changes);           // array of ApiChange objects
console.log(result.formatted);         // formatted output string

Working with individual modules

import {
  parseApiSurface,
  compareApiSurfaces,
  classifyChanges,
  generateReport,
  suggestVersion,
  computeApiStats,
} from 'semver-check';

// Parse two versions
const before = parseApiSurface(oldSource, 'index.ts');
const after  = parseApiSurface(newSource, 'index.ts');

// Find what changed
const changes = compareApiSurfaces(before, after);

// Classify breaking / feature / fix
const classified = classifyChanges(changes);

// Suggest version bump
const suggestion = suggestVersion('1.4.2', classified);
console.log(suggestion.next); // "2.0.0"

// Risk scoring
const stats = computeApiStats(after);
console.log(stats.riskScore);          // 0.0 – 1.0
console.log(stats.breakingRuleCount);  // number of triggered breaking rules

// Format as markdown
const report = generateReport(classified, { format: 'markdown' });

TypeScript types

import type {
  ApiSurface,
  ApiChange,
  ChangeClassification,
  SemverSuggestion,
  ApiStats,
  SemverCheckConfig,
} from 'semver-check';

Configuration

Create .semver-check.json in your project root, or add a "semver-check" field to package.json:

{
  "entryPoints": ["src/index.ts"],
  "exclude": ["**/*.test.*", "**/__mocks__/**"],
  "includeInternal": false,
  "strict": false,
  "outputFormat": "text",
  "rules": {
    "deprecation-added": false,
    "param-became-optional": false
  }
}
Field Type Default Description
entryPoints string[] ["src/index.ts"] Files to treat as public API surface
exclude string[] [] Glob patterns to exclude
includeInternal boolean false Include symbols marked @internal
strict boolean false Non-zero exit on any breaking change
outputFormat string "text" Default output format
rules object {} Set individual rules to false to disable

CI Integration

GitHub Actions

Block a release when breaking changes are detected:

name: Semver Check

on:
  pull_request:
    branches: [main]

jobs:
  semver:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Check semver compliance
        run: |
          npx semver-check compare $(git describe --tags --abbrev=0) \
            --strict \
            --format json

Post results as a PR comment:

      - name: Generate API diff
        id: diff
        run: |
          OUTPUT=$(npx semver-check compare $(git describe --tags --abbrev=0) --format markdown)
          echo "report<<EOF" >> $GITHUB_OUTPUT
          echo "$OUTPUT" >> $GITHUB_OUTPUT
          echo "EOF" >> $GITHUB_OUTPUT

      - name: Comment on PR
        uses: actions/github-script@v7
        with:
          script: |
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: `## API Changes\n\n${{ steps.diff.outputs.report }}`
            })

npm preversion hook

Enforce compliance before every npm version command:

{
  "scripts": {
    "preversion": "semver-check compare $(git describe --tags --abbrev=0) --strict"
  }
}

Exit Codes

Code Meaning
0 No issues -- version bump is compliant
1 Breaking changes detected (in --strict mode)
2 Version bump is non-compliant with detected change level
3 Runtime error (file not found, parse failure, etc.)

How It Works

Source Files      Parse           Compare          Classify          Report
────────────      ──────────      ──────────       ──────────        ──────────
src/index.ts  →   Parser       →  Comparator    →  Classifier     →  Reporter
old snapshot      • exports       • added            • breaking        • text
git tag           • functions     • removed          • feature         • json
                  • classes       • modified         • fix             • markdown
                  • interfaces    • renamed
                  • types
                  • enums
  1. Parse -- Extracts the public API surface from TypeScript/JavaScript files using regex-based analysis. No TypeScript compiler required -- works in any environment.
  2. Compare -- Detects added, removed, and modified exports between two versions. Uses LCS-based diff for type definition changes.
  3. Classify -- Maps each change to a semver bump level using 20+ built-in detection rules. Each rule targets a specific kind of breaking change.
  4. Report -- Outputs results in text, JSON, or Markdown format. Includes migration hints, risk scores, and a suggested version bump.

Architecture

src/
  types.ts          # Core type definitions (ApiSurface, ApiChange, etc.)
  parser.ts         # Public API surface extractor (regex-based, no tsc)
  comparator.ts     # Diff engine -- detects added/removed/modified exports
  classifier.ts     # Maps changes to breaking/feature/fix categories
  reporter.ts       # Output formatter (text/json/markdown)
  snapshot.ts       # API snapshot storage and retrieval
  rules.ts          # 20+ detection rules engine
  git-diff.ts       # Git integration (tag resolution, ref checkout)
  config.ts         # Configuration loader (.semver-check.json, package.json)
  differ.ts         # LCS-based line diff and word diff visualization
  type-analyzer.ts  # Deep union/object/array type comparison
  stats.ts          # API statistics and risk scoring
  ci.ts             # CI/CD integration (GitHub Actions outputs, changelog)
  api.ts            # Fluent programmatic builder API
  cli.ts            # CLI entry point
  index.ts          # Public re-exports

Test Coverage

334 tests across 16 categories:

Category Tests Coverage
Parser (basic + advanced + JS) 66 Exports, functions, classes, interfaces, types, enums
Comparator (unit + edge cases) 43 Added/removed/modified, renames, edge cases
Classifier 25 All 20+ rules, bump level assignment
Reporter 21 text/json/markdown output, migration hints
Snapshot 11 Save, load, label, list
Config 19 JSON file, package.json field, defaults, merging
Differ 17 LCS, word diff, tokenizer
Type Analyzer 25 Union, object, array, generic type comparison
Rules 14 Rule engine, rule toggling, custom rules
Stats 12 Risk score, API size, complexity metrics
CI 23 GitHub Actions output, exit codes, PR summary
Git Diff 13 Tag resolution, ref checkout, real git repos
Integration 10 End-to-end compare flow
API (fluent builder) 11 Builder API, method chaining
Scenarios 10 Real-world Express, ORM, plugin, enum patterns
Compliance 14 Version suggestion accuracy, bump validation

FAQ

Q: Does this require a TypeScript compiler? A: No. semver-check uses regex-based parsing and works on any .ts or .js file without tsc installed. This makes it fast and universally portable in CI environments.

Q: How does it compare against a previous version? A: You have three options -- a saved snapshot (semver-check snapshot), a git tag (compare v1.0.0), or two explicit references (compare v1.0.0 v2.0.0). The git integration checks out the target ref to a temp directory and parses it in place.

Q: Can I use it on JavaScript projects (no TypeScript)? A: Yes. The parser handles plain .js files. JSDoc annotations for types are recognized where present, though type analysis is limited compared to TypeScript.

Q: What is the risk score? A: A number from 0.0 to 1.0 representing overall API stability. It factors in the number of breaking rules triggered, the size of the public surface, and the proportion of the surface that changed. Lower is more stable.

Q: Can I disable specific rules? A: Yes. Set any rule to false in the rules section of your config. For example, "deprecation-added": false ignores deprecation additions when calculating the required bump level.

Q: How is this different from npm diff? A: npm diff shows source code changes. semver-check parses the semantic meaning of those changes -- it tells you whether the changes require a major, minor, or patch bump, not just what lines changed.


Troubleshooting

Problem Likely Cause Solution
No exports found Wrong entry point Check entryPoints in config; default is src/index.ts
Git tag not found Tag doesn't exist locally Run git fetch --tags before comparing
Parse error on line N Unusual TypeScript syntax File an issue with a minimal repro; parser handles common patterns
0 breaking changes but build breaks Dynamic API patterns Add them to entryPoints; dynamic exports are not detected
Config file ignored Wrong filename Use .semver-check.json or "semver-check" key in package.json

License

MIT

About

Detect breaking API changes before they ship. Compares TypeScript/JavaScript API surfaces across versions and flags semver violations.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors