@jsleekr/semver-check
v1.0.0
Published
Detect breaking API changes before they ship. Compares TypeScript/JavaScript API surfaces across versions and flags semver violations.
Maintainers
Readme
🔖 semver-check
Detect breaking API changes before they ship
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 --
--strictmode 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-checkOr run without installing:
npx semver-check compare v1.0.0 v2.0.0 --strictRequirements
- 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 --strictWhat 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.0parse
Display the current API surface -- all exported symbols, their types, parameters, and return values.
semver-check parse
semver-check parse --format jsonExample 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 jsoninit
Scaffold a .semver-check.json configuration file in the current directory.
semver-check initchangelog [base]
Generate a changelog entry from detected API changes.
semver-check changelog 1.0.0
# Outputs a Markdown-formatted changelog blockProgrammatic 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 stringWorking 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 jsonPost 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- Parse -- Extracts the public API surface from TypeScript/JavaScript files using regex-based analysis. No TypeScript compiler required -- works in any environment.
- Compare -- Detects added, removed, and modified exports between two versions. Uses LCS-based diff for type definition changes.
- Classify -- Maps each change to a semver bump level using 20+ built-in detection rules. Each rule targets a specific kind of breaking change.
- 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-exportsTest 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
