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

diffjsons

v0.1.0

Published

A structural JSON diff for humans and CI.

Downloads

32

Readme

jsondiff

A structural JSON diff for humans and CI.

jsondiff compares two JSON documents by structure and values, not by text. Key order, whitespace, and formatting do not matter. The result is a readable terminal diff, compact one-liners, a summary, or deterministic JSON — plus reliable exit codes for scripts.

Why structural diffing matters

A line-by-line text diff treats these as completely different files:

{"name":"John","age":31}
{
  "age": 31,
  "name": "John"
}

They are the same JSON value. jsondiff reports them as equal.

When something actually changes, the tool points at the path — user.profile.name, tags[2], ["user.name"] — instead of dumping two whole objects.

Installation

npm i -g diffjsons

That installs the diffjsons command.

Or run it without installing:

npx diffjsons old.json new.json

Requires Node.js 20 or later.

Quick start

diffjsons old.json new.json
diffjsons

old.json → new.json
────────────────────────────────────────────

tags[2]
  + "opensource"

user.address.city
  + "Toronto"

user.age
  - 31
  + 32

user.name
  - "John"
  + "John Smith"

users[0].email
  - "[email protected]"
  + "[email protected]"

Summary
────────────────────────────────────────────
  + 2 additions
  - 0 removals
  ~ 3 modifications

Status: CHANGED

Supported JSON types

Objects, arrays, strings, numbers, booleans, null, and any nesting of those.

Comparison uses JSON.parse values. 1 and 1.0 are equal because they parse to the same JavaScript number. There is no floating-point tolerance.

Object comparison

Objects are compared by key. Key order in the file does not affect the result.

diffjsons <(echo '{"b":2,"a":1}') <(echo '{"a":1,"b":2}')

Exit code 0.

Array comparison

Arrays are not stringified and marked changed as a whole.

Default strategy: LCS-anchored leftover pairing.

  1. Find a longest common subsequence of deep-equal elements (anchors).
  2. Between anchors, pair leftover elements in order and recurse.
  3. Extra leftovers are additions or removals.

This is why an insert is an insert:

["a", "b", "c"] vs ["a", "x", "b", "c"]  →  [1] + "x"

And why an object field change stays a field change:

users[0].name
  - "John"
  + "John Smith"

Reordered arrays ([1,2,3] vs [3,2,1]) are differences. Use --sort-arrays if order should not matter.

If both array lengths multiply to more than 4,000,000, jsondiff falls back to index alignment.

Match object arrays by key

id is not special. Identity matching is opt-in:

diffjsons old.json new.json --array-key id
  • Elements are matched by JSON.stringify of that property.
  • Duplicate keys on the same side are an input error (exit 3).
  • A missing key makes that element unmatched (added or removed).
  • If the array contains non-objects, the default LCS algorithm is used instead.

Ignore paths

diffjsons old.json new.json --ignore metadata.timestamp
diffjsons old.json new.json --ignore "metadata.*" --ignore "users.*.password"

Syntax:

| Pattern | Meaning | |---|---| | metadata.timestamp | That path and nothing else | | metadata.* | Every direct child of metadata (and their subtrees) | | metadata | The whole metadata subtree, including add/remove | | users[0].name | Index sugar | | users.*.password | Password on every user | | ["user.name"] | A literal key that contains a dot |

* matches exactly one path segment. There is no ** and no jq/JSONPath.

Sort arrays

diffjsons old.json new.json --sort-arrays

Each array is copied and sorted for comparison only. Input files are never rewritten.

  • Duplicates are kept (multiset).
  • Sort order: null, boolean, number, string, array, object; then a deterministic value order.
  • Paths use post-sort indices.

Output modes

diffjsons old.json new.json --compact
diffjsons old.json new.json --summary
diffjsons old.json new.json --json
diffjsons old.json new.json --quiet

--compact:

+ user.address.city = "Toronto"
~ user.name: "John" → "John Smith"
- user.age = 31

--json is deterministic: no timestamps, no absolute paths, no ANSI. The path field uses the same formatted paths as the terminal ("" for the document root).

--quiet prints nothing. Use the exit code.

Only one of --json, --summary, --compact, --quiet may be set.

Color

Default: color when stdout is a TTY.

  • --color forces ANSI
  • --no-color disables ANSI (wins if both are passed)
  • NO_COLOR (any non-empty value) disables color unless --color is set

JSON and quiet modes never emit ANSI.

Stdin

stdin is the old document. Exactly one file is required: the new document.

diffjsons --stdin new.json < old.json
cat old.json | diffjsons --stdin new.json

Two files plus --stdin, or --stdin with no file, is a usage error.

Exit codes

| Code | Meaning | |---|---| | 0 | Documents are identical | | 1 | Documents differ | | 2 | Invalid usage | | 3 | Missing file, unreadable file, invalid JSON, or duplicate --array-key |

CI

if diffjsons expected.json actual.json --quiet; then
  echo "JSON matches"
else
  echo "JSON differs"
  exit 1
fi

Library API

import { diffJson, formatPath } from "diffjsons";

const result = diffJson(
  { name: "John", tags: ["dev"] },
  { name: "John Smith", tags: ["dev", "oss"] },
);

console.log(result.changed);
console.log(result.summary);
console.log(formatPath(result.changes[0].path));
diffJson(left, right, {
  sortArrays?: boolean;
  arrayKey?: string;
  ignore?: string[];
})

The CLI uses this same function. There is no second comparison implementation.

Numbers

Equality follows JavaScript JSON.parse:

  • 1 and 1.0 are equal
  • 0 and -0 are equal
  • Integers beyond Number.MAX_SAFE_INTEGER may lose precision
  • There is no epsilon / tolerance

Performance

Designed for developer-sized JSON (configs, API fixtures, snapshots).

  • 32 MiB maximum per input
  • LCS for arrays until len(left) * len(right) > 4_000_000, then index alignment
  • No streaming parser

Limitations

  • Not a text diff; whitespace-only changes are ignored
  • Reordered arrays are differences unless --sort-arrays
  • --sort-arrays paths use post-sort indices
  • --array-key is opt-in; id is not inferred
  • Very large arrays use index alignment
  • JavaScript IEEE-754 numbers only
  • Max 32 MiB per file
  • JSON only — no YAML, TOML, or XML
  • No RFC 6902 patch output

No telemetry

jsondiff makes no network requests. Comparison happens entirely on your machine. There is no analytics, tracking, or phone-home.

Development

npm install
npm test
npm run lint
npm run typecheck
npm run build

See CONTRIBUTING.md.

License

MIT