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

sniffler

v0.5.0

Published

Sniffler sniffs out the E2E tests your changes actually wake up.

Downloads

4,297

Readme

Sniffler

Sniffler sniffs out the E2E tests your changes actually wake up.

mit licence npm downloads Chat PRs Welcome

Sniffler turns changed source files into the E2E tests they can affect so you can run a smaller, more relevant set in CI or locally without tracing the whole graph by hand.

Features

  • Impact Selection: Resolve which E2E tests are affected by changed files across TypeScript and JavaScript projects.

  • Fast Development Workflow: Use impact to inspect the affected tests and run to append them to your existing runner command.

  • Workspace Awareness: Understand package and workspace layouts so impacted tests are found across real-world monorepos.

  • React Native Platform Resolution: Pass a runtime platform such as android to resolve .android.*, .native.*, and generic source files in Metro-style order.

  • Configurable Cache: Store graph data in .sniffler/cache.json to speed up repeated runs.

  • Type-Safe & Developer-Friendly: The CLI, graph, cache, and fixture-backed tests are all written in TypeScript so the behavior stays easy to evolve.

  • Works With Your Setup: Works with Git diffs, uncommitted (staged, unstaged, and untracked) changes, explicit file lists, package.json workspaces, pnpm-workspace.yaml, TSConfig path aliases, and package exports.

Getting started

Install Sniffler in your project:

pnpm add -D sniffler
# or
npm install -D sniffler

Add Sniffler's project files to your repo:

.sniffler/config.json
.sniffler/test-map.json

Start with a focused config that points Sniffler at your source roots and test map:

{
  "source": {
    "roots": ["apps", "packages"],
    "ignore": ["**/*.test.*", "**/*.spec.*", "**/__tests__/**"]
  },
  "tests": {
    "manifest": ".sniffler/test-map.json"
  }
}

Sniffler fills in defaults for source extensions, workspace discovery, TSConfig paths, cache behavior, test selection rules, and output format. See docs/config.md for the full configuration reference.

If you have a shared setup module that reaches into the rest of the app, add it under tests.sharedTargets so Sniffler still walks the dependency graph from that module into its imports. For example, when src/global.ts imports src/some-other.ts, a change to src/some-other.ts can still select every test that shares src/global.ts.

If a module should invalidate its descendants when touched, add it under tests.invalidateSubtreeWhenTouched. Sniffler first finds the usual reverse dependency impact, then starts a forward walk from any touched module that matches one of those paths or globs. That keeps containment explicit and still grounded in the import graph.

If your router or app shell composes files through runtime conventions, include those files in source.roots and describe subtree relationships with graph.contains. Sniffler will add synthetic containment edges and show synthetic hops in impact output when they drive a test selection.

If a lockfile or other repo-level file should force every test, add tests.runAllWhenChanged:

{
  "tests": {
    "manifest": ".sniffler/test-map.json",
    "runAllWhenChanged": ["pnpm-lock.yaml"]
  }
}

Sniffler checks this before dependency analysis, so a match skips workspace discovery, source scanning, graph build, and cache work.

The test map now uses a plain array of entries with test and dependsOn. Sniffler auto-converts the legacy { "tests": [...] } shape on first read so older repos keep working.

Then run the CLI from the project root:

pnpm exec sniffler impact --base origin/main --head HEAD
pnpm exec sniffler run --base origin/main --head HEAD -- pnpm vitest run

For local work, discover staged, unstaged, and untracked non-ignored files directly:

pnpm exec sniffler impact --uncommitted
pnpm exec sniffler run --uncommitted -- pnpm vitest run

Combine both modes to include committed branch changes and current uncommitted changes in one selection:

pnpm exec sniffler impact --base origin/main --head HEAD --uncommitted
pnpm exec sniffler run --base origin/main --head HEAD --uncommitted -- pnpm vitest run

Positional file lists cannot be combined with --base/--head or --uncommitted.

You can also pass files directly when you are not starting from a Git diff:

sniffler impact src/components/Button.tsx
sniffler run src/components/Button.tsx -- pnpm vitest run

For React Native projects, pass a runtime platform to resolve extensionless imports with Metro-style platform priority:

sniffler impact --platform android src/components/Button.android.tsx
sniffler run --platform android src/components/Button.android.tsx -- pnpm vitest run

sniffler run appends impacted test files to the runner command and exits with the runner's exit code.

Quick example

sniffler impact --base origin/main --head HEAD
sniffler run --base origin/main --head HEAD -- pnpm vitest run
sniffler impact --uncommitted
sniffler run --uncommitted -- pnpm vitest run

Use impact when you want to inspect the affected set first. Use run when you want Sniffler to pass that set directly into your test runner.

Project compatibility

Sniffler currently supports:

  • Git-based workflows: Diff-based, uncommitted, combined, and explicit file-based impact selection.
  • JavaScript and TypeScript: Source graphs built from JS/TS files.
  • Monorepos: Workspace package discovery through package.json and pnpm-workspace.yaml.
  • TypeScript path aliases: Resolution through configured TSConfig path mappings.
  • React Native platforms: Optional runtime platform probing for extensionless relative imports and TSConfig aliases.
  • Package exports: Workspace package exports and package-name imports in supported setups.

Made with ❤️ at Callstack

sniffler is open source and free to use. If it helps your workflow, please star it 🌟. Callstack is a team of React and React Native engineers, contact us at [email protected] if you need help or just want to say hi.

Like the project? ⚛️ Join the team who does amazing stuff for clients and drives React Native Open Source! 🔥