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.
-
Impact Selection: Resolve which E2E tests are affected by changed files across TypeScript and JavaScript projects.
-
Fast Development Workflow: Use
impactto inspect the affected tests andrunto 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
androidto resolve.android.*,.native.*, and generic source files in Metro-style order. -
Configurable Cache: Store graph data in
.sniffler/cache.jsonto 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, explicit file lists,
package.jsonworkspaces,pnpm-workspace.yaml, TSConfig path aliases, and package exports.
Install Sniffler in your project:
pnpm add -D sniffler
# or
npm install -D snifflerAdd 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 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.
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 runYou 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 runFor 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 runsniffler run appends impacted test files to the runner command and exits with the runner's exit code.
sniffler impact --base origin/main --head HEAD
sniffler run --base origin/main --head HEAD -- pnpm vitest runUse 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.
Sniffler currently supports:
- Git-based workflows: Diff-based and explicit file-based impact selection.
- JavaScript and TypeScript: Source graphs built from JS/TS files.
- Monorepos: Workspace package discovery through
package.jsonandpnpm-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.
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 hello@callstack.com 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! 🔥
