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

dart-decimate

v0.0.14

Published

Rust-native codebase intelligence for Dart and Flutter module graphs.

Readme

Dart Decimate

Dart Decimate banner

Find dead Dart code, circular dependencies, duplicated code, complex functions, dependency problems, risky Flutter wiring, and PR risk fast.

Rust 1.85+ Dart and Flutter License

Dart Decimate is a Rust-native codebase intelligence tool for Dart and Flutter. It looks at your repo as a graph:

  • Dart files are nodes.
  • import, export, part, part of, and library augment are edges.
  • The report tells you what is unused, risky, duplicated, tangled, or hard to maintain.

It is not a formatter. It is not a replacement for dart analyze. It is not a Flutter style guide. It does not enforce opinions like "all providers must use Riverpod code generation."

It answers practical questions:

  • What code can probably be deleted?
  • What files depend on each other in a circle?
  • What functions are too complex?
  • What code was copied around?
  • What dependency is unused or missing from pubspec.yaml?
  • What changed code is risky before a PR lands?
  • What should an AI coding agent inspect before making a fix?

Start Here

Inside any Dart or Flutter project, run this:

npx --yes dart-decimate human .

That is the easiest command. It checks the whole repo for dead code, circular dependencies, duplicated code, complex functions, dependency hygiene, architecture drift, Flutter graph issues, security candidates, and PR-risk signals.

To open the same report in your browser:

npx --yes dart-decimate html .

To review only files changed from another branch or ref:

npx --yes dart-decimate html . --compare origin/main

--compare REF aliases --changed-since REF. If the ref is not found, Dart Decimate suggests similar local or remote branches.

For JSON output that agents and CI can parse:

npx --yes dart-decimate json .

If the human report says FAIL, or JSON says "verdict": "fail", Dart Decimate worked. It means it found error-level issues. It does not mean the tool crashed.

Exit codes:

  • 0: no error-level findings
  • 1: Dart Decimate found issues
  • 2: command, config, or runtime error
  • 8: security gate found new review-required candidates

Install

You do not need to install anything permanently. Use npx:

npx --yes dart-decimate human .

Add this to package.json if you want a short project command:

{
  "scripts": {
    "dart-decimate": "dart-decimate json ."
  },
  "devDependencies": {
    "dart-decimate": "^0.0.14"
  }
}

Then run:

npm run dart-decimate

If you prefer Cargo:

cargo install --git https://github.com/sgaabdu4/dart-decimate

Cargo installs dart-decimate and dart-decimate-mcp into ~/.cargo/bin.

Fish:

fish_add_path ~/.cargo/bin

Bash or Zsh:

export PATH="$HOME/.cargo/bin:$PATH"

From a local checkout:

git clone https://github.com/sgaabdu4/dart-decimate.git
cd dart-decimate
cargo install --path . --force

Then run it in your app:

cd /path/to/flutter_or_dart_repo
dart-decimate check . --format json

npx

The npm package name is dart-decimate. These commands do not require a global install.

Check everything:

npx --yes dart-decimate human .

Machine-readable JSON:

npx --yes dart-decimate json .

Open the HTML report in your browser:

npx --yes dart-decimate html .

The html shortcut opens the report by default. On report commands, use --format html to print static HTML or --open to write a private temporary file and open it in the default browser.

Generated finding reports group issues by type in numbered, collapsible sections. Search narrows visible findings, and type filters open the selected group.

Human terminal reports strip control characters from user-derived paths and messages; HTML reports escape user-derived content.

Print the HTML report instead:

npx --yes dart-decimate html . --stdout > dart-decimate-report.html

Changed files only: npx --yes dart-decimate html . --compare origin/main. --compare REF aliases --changed-since REF and suggests similar branches when the ref is not found.

To run the GitHub version directly:

npx --yes --package github:sgaabdu4/dart-decimate dart-decimate check . --format json

What Dart Decimate Looks For

1. Dead Code

Dead code is code that is not reachable from your entry points.

Dart Decimate finds:

  • dead Dart files
  • unused public exports
  • unused type aliases
  • unused enum values
  • unused private class members
  • unrendered Flutter widgets
  • missing entry points
  • stale dart-decimate-ignore comments

Default entry points are evaluated across the root and discovered local package roots. They include public lib/ Dart files outside lib/src/, lib/main.dart, direct bin/ scripts, and, outside production mode, direct test/, integration_test/, test_driver/, tool/, scripts/, and pigeon/ scripts. Nested test/, integration_test/, and test_driver/ files with main() are also roots, including files matching Patrol's configured test_file_suffix. Git-ignored Dart files are excluded from source analysis. Generated Dart companions, Flutter l10n outputs, and FlutterFire options are protected from dead-file cleanup; generated-only cycles are also suppressed.

Unused-symbol checks count references from non-raw Dart string interpolation; raw strings and escaped dollars stay literal.

Useful commands:

dart-decimate dead-code . --entry lib/main.dart --format json
dart-decimate check . --unused-files --unused-exports --unused-deps --format json

2. Complex Code

Cyclomatic complexity means "how many paths can this function take?"

Cognitive complexity means "how hard is this function to understand?"

CRAP score combines complexity with test coverage. A complex function with poor coverage gets a worse score.

Dart Decimate finds:

  • high cyclomatic complexity
  • high cognitive complexity
  • high combined complexity
  • high CRAP score
  • coverage gaps
  • low health score files
  • hotspots
  • refactoring targets

Useful commands:

dart-decimate health . --format json
dart-decimate health . --complexity-breakdown --top 10 --format json
dart-decimate health . --file-scores --hotspots --targets --format json

3. Duplicated Code

Duplication means the same Dart code appears in more than one place.

Dart Decimate finds exact and semantic clone groups. Each clone group gets a stable fingerprint like dup:abc12345, so agents can trace it before touching code. Clone windows must meet both line and token thresholds; sparse duplicated blocks can span more than --min-lines, and line_count reports the actual match. Declaration-only abstract contracts are filtered, and copied local Pub package mirrors are canonicalized before same-mirror matches are ignored.

Useful commands:

dart-decimate dupes . --format json
dart-decimate dupes . --mode semantic --min-lines 5 --format json
dart-decimate dupes . --threshold 5 --format json
dart-decimate trace-clone . --fingerprint dup:abc12345 --format json

4. Circular Dependencies

A circular dependency means file A depends on file B, and B eventually depends back on A. Cycles make code harder to move, test, and delete.

Dart Decimate finds:

  • circular dependencies
  • re-export cycles
  • import/export/part/augment targets that do not resolve
  • invalid part / part of relationships

Generated typed GoRouter route registry back-edges are warning-level only when the importing file uses real typed-route navigation helpers. Mixed cycles still emit error-level residual findings for unrelated edges, registry-to-registry imports, exports, or non-route registry API usage.

Useful commands:

dart-decimate cycles . --format json
dart-decimate check . --circular-deps --re-export-cycles --format json

5. Architecture Drift

Architecture drift means code crosses boundaries it should not cross.

Example: lib/domain/ depending on lib/ui/.

Dart Decimate finds:

  • boundary violations
  • files outside configured boundary zones
  • forbidden direct calls across boundaries
  • policy-pack violations for banned imports, exports, and calls

Useful command:

dart-decimate check . \
  --boundary lib/domain:lib/ui \
  --boundary-coverage \
  --format json

6. Dependency Hygiene

Dependency hygiene means your imports and pubspec.yaml agree.

Dart Decimate finds:

  • unused runtime dependencies
  • unused dev dependencies
  • runtime dependencies used only by tests
  • imports missing from pubspec.yaml
  • unused dependency overrides
  • invalid dependency overrides
  • imports into another package's private lib/src
  • duplicate public API exports

Dart Decimate follows Pub package ownership when resolving package: imports, using .dart_tool/package_config.json when present while keeping same-package imports and owner-local path dependencies local. Workspace members and copied nested packages with the same package name are resolved by owner. Non-Dart tooling references in Flutter config files, including launcher and splash config, workflow files, Makefiles, and tool/ scripts can count as dependency usage. Envied annotations count envied_generator/build_runner, FlutterGen configuration counts flutter_gen_runner, and Dart tests count the test runner. Known generator-internal imports from generated Dart, such as package:slang/generated.dart, are not reported as unlisted dependencies.

Useful commands:

dart-decimate trace-dependency . --dependency collection --format json
dart-decimate check . --unused-deps --unlisted-deps --private-src-imports --format json

7. Flutter-Specific Graph Issues

Dart Decimate does not care which state-management style you use. It uses Flutter and Dart patterns only to avoid false positives and to find graph problems.

Dart Decimate finds:

  • GoRouter route path/name collisions
  • private Flutter widget classes
  • top-level widget helper functions
  • unused widget constructor parameters
  • widget classes that are never constructed
  • missing context.mounted guards after awaited widget work

unused-widget-param counts normal field reads, transformed constructor initializer reads, inherited field reads, forwarding helpers, and Dart object patterns that destructure the widget class itself. unrendered-widget counts widgets constructed through builder callbacks and subclass construction, while type-only references do not count as rendering.

8. Security Candidates

These are review prompts, not proof of an exploit.

Dart Decimate finds candidates for:

  • hardcoded secrets
  • insecure HTTP transport
  • TLS validation bypasses
  • risky WebView settings
  • process execution
  • raw SQL
  • Firebase client API keys in FirebaseOptions
  • plain local storage of secret-like material

Firebase client API keys are warning-level by default because FlutterFire generates client config. To make them fail a gate, set "dart-decimate/security-firebase-api-key" = "error" in [rules]. Common authentication copy such as password reset and password requirement text is filtered before reporting hardcoded-secret candidates unless it is bound to a secret-like name or contains a concrete token-like segment. OAuth authorization and token endpoint URLs are excluded from secret candidates as public metadata, while cleartext endpoint transport remains an insecure-transport candidate. Stripe secret-key names and sk_test_/sk_live_ values remain review candidates even when they look like placeholders. A fixed Process.start of Platform.resolvedExecutable with fixed list arguments is not classified as shell command injection.

Useful commands:

dart-decimate security . --surface --format json
dart-decimate security . --ci --sarif-file dart-decimate-security.sarif
git diff --cached --unified=0 | dart-decimate security . --gate new --diff-stdin --format json
dart-decimate security . --gate newly-reachable --compare origin/main --format json

--gate new keeps candidates on added lines. --gate newly-reachable keeps reachable candidates affected by changed files. Both accept --compare REF, --changed-since REF, --diff-file PATCH, or --diff-stdin.

9. PR Risk

Use this before merging changed code.

Dart Decimate reports:

  • risk score
  • pass / warn / fail risk level
  • findings introduced by the PR
  • findings that already existed
  • risky changed files

Compile-time environment feature flags are warning-level only when every occurrence is in a non-lib/ development, tooling, example, or test path, or in lib/main_dev.dart, lib/main_debug.dart, lib/main_e2e.dart, lib/main_test.dart, or lib/main_driver.dart. Other lib/ files, including lib/dev_flags.dart, services, and screens, remain error-level. SDK/config flag calls stay error-level by default.

Useful commands:

dart-decimate audit . --base origin/main --format json
dart-decimate audit . --base origin/main --gate new-only --format json

10. Runtime Intelligence

Static analysis says what is connected. Runtime coverage says what actually ran.

Dart Decimate can read LCOV, V8, and Istanbul coverage data.

Useful commands:

dart-decimate health . --coverage coverage/lcov.info --coverage-gaps --max-crap 30 --format json
dart-decimate coverage analyze . --runtime-coverage coverage-final.json --format json

How To Read The Summary

Example:

{
  "files": 466,
  "edges": 1231,
  "quality_score": 93,
  "cycles": 2,
  "code_duplications": 26,
  "complex_functions": 13,
  "dead_files": 11,
  "findings": 125
}

Plain English:

  • files: Dart files Dart Decimate parsed
  • edges: imports, exports, parts, and augments it resolved
  • quality_score: project health from 0 to 100
  • cycles: emitted circular dependency findings after policy classification
  • code_duplications: duplicated code groups
  • complex_functions: functions over the complexity limits
  • dead_files: files Dart Decimate thinks are unreachable
  • findings: total issues in the report

JSON For Agents

Use JSON when another tool or AI agent will read the result:

dart-decimate check . --format json

Every finding includes:

  • rule_id
  • kind
  • severity
  • path
  • line
  • column
  • safe_to_delete
  • related files
  • related dependency edge, when available
  • suggested actions

Example shape:

{
  "schema_version": "dart-decimate.report.v1",
  "kind": "combined",
  "tool": "dart-decimate",
  "command": "check",
  "verdict": "fail",
  "summary": {
    "files": 466,
    "edges": 1231,
    "quality_score": 93,
    "findings": 125
  },
  "findings": [],
  "next_steps": []
}

When grouped security findings hide additional occurrences, next_steps can include review-security-surface, which reruns dart-decimate security . --format json --surface.

If a JSON command fails before a report can be built, stdout still stays machine-readable:

{ "error": true, "message": "coverage analyze requires --runtime-coverage PATH", "exit_code": 2 }

Fixes

Preview safe fixes:

dart-decimate fix . --format json

Apply confirmed safe fixes:

dart-decimate fix . --apply --confirm --format json

Safe fixes are intentionally conservative. Dart Decimate can currently apply:

  • simple dead-file deletion
  • stale suppression removal
  • one-line unused Pub dependency removal
  • one-line unused top-level Dart declaration removal

Everything else stays review-only.

Watch Mode

Rerun checks while you work:

dart-decimate watch . --no-clear

Run once and exit, useful for scripts:

dart-decimate watch . --once --format json

MCP

Start the MCP server:

dart-decimate-mcp

Agents can use it to inspect a project, trace files, trace symbols, inspect duplicates, review PR risk, read runtime coverage slices, preview fixes, and ask what is safest to do next.

fix_apply is the only mutating MCP tool and requires explicit yes: true.

Config

Dart Decimate reads config from:

  1. .dart-decimaterc
  2. .dart-decimaterc.json
  3. .dart-decimaterc.jsonc
  4. dart-decimate.toml
  5. .dart-decimate.toml

Example:

[cli]
format = "json"
entry = ["lib/main.dart"]
production = true

[health]
max_cyclomatic = 20
max_cognitive = 15
coverage_gaps = true
fileScores = true
hotspots = true
targets = true

[dupes]
mode = "semantic"
min_tokens = 80
threshold = 5

[boundaries]
presets = ["layered"]
rules = ["lib/domain:lib/ui"]

[security]
surface = true
categories = ["hardcoded-secret", "firebase-api-key", "insecure-transport", "tls-bypass"]

[rules]
unused-files = "error"
unused-exports = "warn"
security-candidate = "warn"
"dart-decimate/security-firebase-api-key" = "error"

Full Issue List

Run this for the live list:

dart-decimate schema --format json | jq .issue_types

Current issue types:

See docs/issue-types.md. Prefer the schema command when you need the installed binary's exact list.

CI

See docs/ci.md for CI checks, PR gates, hook setup, CI templates, and review-thread reconciliation.

Scope

Dart Decimate implements local Fallow-style codebase intelligence for Dart and Flutter.

Fallow features that are JS-specific or require hosted backends return clear unsupported JSON instead of pretending to work:

dart-decimate migrate --dry-run --format json
dart-decimate telemetry status --format json
dart-decimate license status --format json

Development

See docs/ci.md for the local verification commands and CI gates.

This repository forbids unsafe_code.

Release Flow

Current version: 0.0.14.

After the first public release, changes should go through pull requests. Every PR to main must bump both Cargo.toml and package.json above the base branch and to an unpublished npm version.

To release a new version:

  1. Update both Cargo.toml and package.json to the same unpublished version.
  2. Open a PR.
  3. Let CI pass.
  4. Merge to main.
  5. GitHub Actions publishes dart-decimate to npm, creates tag vX.Y.Z, and creates the GitHub release.

Release reruns for the same commit may update GitHub release assets. If the npm package already exists for that commit, the publish step is skipped; a reused tag or npm version from another commit fails.

Local hooks block direct pushes to main, fetch the configured base ref when needed, and run the same version, release, lint, Fallow, package, and test gates as the local no-mistakes configuration.

License

Licensed under the MIT License. See LICENSE.