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

@ossl-dev/differens-tiers

v0.2.0

Published

Content-tier adapters that parse code, config and markup into trees the differens core can match

Readme

@ossl-dev/differens-tiers

Turns a file into a tree that @ossl-dev/differens-core can match, picking the parser from the path.

Part of differens.

npm install @ossl-dev/differens-tiers

Diffing two files

import { diffWithTier } from "@ossl-dev/differens-tiers";

const { changes, tier, nodeCount } = diffWithTier(
  oldSource,
  newSource,
  "src/app.ts",
  "src/app.ts",
);

The paths are how the tier is chosen, so pass the real ones even when the bytes came from somewhere else -- a git blob, a temp file, a buffer. Handing it the name of a scratch file with no extension gets you a line diff of TypeScript.

The ladder

Six tiers, each falling back to the one below when it cannot parse:

| Tier | Handles | Produces | |---|---|---| | Code | ts, tsx, js, py, rs, go, and ~25 more by extension | tree-sitter CST, with semantic labels for TS/JS, Python, Rust and Go | | Data | json, jsonc, yaml, yml, toml, ini | value trees, so database.pool.max is one changed leaf | | Markup | html, xml, svg, plist | element trees keyed on id/class | | Prose | txt, log, LICENSE, README | word-level diff | | Raw | everything else, and markdown | LCS line diff | | Binary | by extension, or sniffed from NUL bytes | size comparison only |

A tier is used only if it both parses and returns a usable tree diff, so an oversized file does not report "no changes" -- it falls through to the line diff that can actually answer.

Which files are worth parsing

import { isParseable, hasGrammar, classifyFile } from "@ossl-dev/differens-tiers";

isParseable("src/app.ts");   // true  -- a real tree comes out
isParseable("notes.md");     // false -- line diff either way
hasGrammar("rs");            // is the Rust grammar installed
classifyFile("config.yaml"); // { path, extension: "yaml", tier: Tier.Data }

isParseable is the one worth reaching for before you spend money on a file. Parsing dominates the cost of a diff, so a changeset of files that will only ever be line-diffed is not worth handing to a worker pool -- it finishes sooner inline than the workers take to start.

Grammars

The tree-sitter grammars are native addons, loaded synchronously the first time an extension is seen. Diffing a Python changeset never pays for the Rust and Go grammars.

They cannot be embedded in a single-file compiled executable. In one, this package falls back to line diff unless it is run somewhere the grammars are installed.

Also exported

parseCode(source, extension) and parseData(source) return a Node tree directly, if you want the tree without the diff. treeFromValue(value) builds one from an in-memory object. getExtractors() lists what has semantic support versus generic CST node types.

Related

MIT. Source.