code_quality_cli 1.0.3
code_quality_cli: ^1.0.3 copied to clipboard
Installable CLI that audits a Flutter/Dart project's assets, source code, and pubspec dependencies with a shared, colorized report format.
code_quality_cli #
A single, installable CLI that audits a Flutter/Dart project's assets,
source code, and pubspec dependencies — one discoverable command,
a shared colorized report format, and a --json mode CI can gate on.
Install #
dart pub global activate code_quality_cli
code_quality_cli --help
This puts code_quality_cli on your PATH, usable from any Flutter/Dart
project via --path (defaults to the current directory).
From source #
To run a checkout of this repo instead of the published package (e.g. while developing it):
dart pub get
dart run bin/code_quality_cli.dart --help
Or activate that local checkout globally:
dart pub global activate --source path .
code_quality_cli --help
Usage #
code_quality_cli <command> [options]
| Command | What it checks |
|---|---|
assets |
Unused assets, broken (missing/unregistered) references, dynamic references |
code |
Unused files, comment-heavy files, commented-out dead code, largest files |
packages |
Unused/missing/unpinned dependencies, dev-dep-in-lib and lib-dep-in-test-only miscategorization |
all |
Runs all three and prints a combined scorecard |
Run with no arguments in an interactive terminal and it will prompt you for
which audit to run (and, for code/all, the comment-density limit) instead
of just printing usage:
$ code_quality_cli
? Which audit would you like to run? (Use arrow keys)
❯ All (assets + code + packages)
Assets
Code
Packages
In a non-interactive context (CI, piped output, --json), this fallback
never fires — invoking with no subcommand just prints usage and exits 2,
so a CI job can never hang waiting on stdin.
Global options #
| Flag | Default | Meaning |
|---|---|---|
-p, --path=<dir> |
. |
Project root to audit |
--json |
off | Emit machine-readable JSON instead of the report |
--no-color |
(auto) | Disable ANSI colors (auto-disabled outside a terminal) |
--strict |
off | Exit non-zero on warnings too, not just errors |
-v, --verbose |
off | Show full lists instead of truncating to 10 items per section |
-h, --help |
Print usage |
code-only options #
| Flag | Default | Meaning |
|---|---|---|
--comment-limit=<percent> |
20 |
Flag files whose comment lines exceed this percent |
--top=<n> |
10 |
Largest-files leaderboard size |
Examples #
# Audit assets in the current project
code_quality_cli assets
# Audit another repo, failing CI on warnings too
code_quality_cli all --path ../other_app --strict
# Flag files >30% comments, machine-readable output
code_quality_cli code --comment-limit 30 --json
What each audit checks #
assets #
Cross-references the files on disk, the folders registered under
flutter: assets: in pubspec.yaml, and every asset path referenced from
Dart source in lib/.
- Broken references (error) — a literal path referenced in
lib/that either doesn't exist on disk at all, or exists but its folder isn't listed underflutter: assets:, so Flutter won't bundle it. - Unused assets (warning) — files that live under a registered
assets/folder but are never referenced (by literal path or a FlutterGen-generated accessor) anywhere inlib/. - Notes (info) — dynamic/interpolated asset references that can't be resolved statically, and on-disk files outside any registered folder.
This is a heuristic, grep-based scan, not a full static analyzer — see the
doc comment at the top of lib/src/audits/asset_audit.dart for the exact
rules and their limitations (e.g. paths built via string concatenation
aren't recognized).
code #
- Unused files (warning) — files under
lib/with zero incomingimport/exportreferences fromlib/ortest/(excludinglib/main.dart, the entry point). - Comment hygiene (info) — files whose comment-line percentage exceeds
--comment-limit, and runs of 3+ consecutive//lines that look like dead code rather than prose.///documentation comments are not counted toward the density limit — they're documentation, not clutter. - Largest files (info) — the biggest files by line count, up to
--top.
"Unused" means zero incoming references, not "unreachable from main.dart"
— a full transitive-reachability analysis would catch more but risks false
positives on real files (conditional imports, etc.), so this scan
deliberately under-reports rather than over-reports.
packages #
Cross-references dependencies:/dev_dependencies: in pubspec.yaml
against every package: import found in the codebase.
- Missing dependencies (error) — imported somewhere but not declared directly (only resolvable today because something else pulls it in transitively — one version bump upstream and the build breaks).
- Dependency hygiene (warning):
- Unused — declared under
dependencies:but never imported.dev_dependencies:is exempt from this check: build tools and codegen (build_runner,lints,flutter_launcher_icons, ...) are legitimately consumed via CLI/build.yaml, never a Dartimport, and would otherwise always show up as false positives. - Unpinned — no version constraint at all.
- A
dev_dependencies:entry imported fromlib/(miscategorized — it's shipping in the app, not just used for building/testing). - A
dependencies:entry only ever imported fromtest/(candidate to move todev_dependencies:).
- Unused — declared under
Sample output #
📋 Combined audit — my_app
┌───────────────────────┬───────┐
│ Broken references │ 2 ❌ │
│ Unused assets │ 12 ⚠️ │
│ Notes │ 1 ℹ️ │
│ Unused files │ 3 ⚠️ │
│ Comment hygiene │ 4 ℹ️ │
│ Largest files │ 10 ℹ️ │
│ Missing dependencies │ 1 ❌ │
│ Dependency hygiene │ 6 ⚠️ │
│ Reclaimable │ 484 KB │
└───────────────────────┴───────┘
📦 Asset
❌ Broken references (2)
assets/images/store.png
└─ lib/src/checkout/receipt_page.dart:65
...
❌ Issues found — see errors above.
--json output #
Every command accepts --json for machine-readable output instead of the
colorized report — useful for CI gating or piping into another tool:
$ code_quality_cli assets --path . --json
{
"tool": "assets",
"summary": { "Reclaimable": "484.1 KB" },
"sections": [
{
"title": "Broken references",
"findings": [
{
"severity": "error",
"message": "assets/images/store.png",
"location": "lib/src/checkout/receipt_page.dart:65",
"detail": "file doesn't exist"
}
]
}
],
"hasErrors": true,
"hasWarnings": false
}
all --json wraps the same shape as {"reports": [...], "hasErrors": ..., "hasWarnings": ...}, one entry per audit.
Exit codes #
| Code | Meaning |
|---|---|
0 |
No errors (warnings are fine unless --strict) |
1 |
At least one error-level finding (or any finding, under --strict) |
2 |
Tool failure — bad arguments, unreadable/missing project |
CI usage #
A GitHub Actions recipe is included at
.github/workflows/code_quality.yml:
it runs dart analyze/dart test on this repo, then runs
code_quality_cli all --json --strict and uploads the JSON report as a
build artifact, failing the job on any finding. Point --path at a
different checkout to audit another repo instead of this one.
Programmatic use #
Each audit is a pure, injectable function (auditAssets, auditCode,
auditPackages) that only touches the filesystem under a given repoRoot,
independent of the CLI:
import 'package:code_quality_cli/code_quality_cli.dart';
final result = auditAssets(repoRoot: '/path/to/project');
print(result.unusedAssets);
// Or work with the same shared report model the CLI renders:
final report = result.toReport();
print(report.hasErrors);
Architecture #
lib/src/
audits/ auditAssets / auditCode / auditPackages — pure functions, one
result class + a toReport() mapper per audit. No CLI/rendering
concerns; each is independently testable against a repoRoot.
report/ Finding / AuditSection / AuditReport — the shared vocabulary
every audit's toReport() translates into, plus Renderer, the
one place terminal/JSON formatting lives.
cli/ CodeQualityCommandRunner (package:args CommandRunner), the four
Command subclasses, and command_support.dart's shared
run → render → exit-code pipeline.
The audits know nothing about the CLI or the report model; the CLI knows
nothing about how an audit works, only that it produces an AuditReport.
Adding a new audit means: write a pure auditX({required repoRoot})
function, add a toReport() extension, and wire up one Command — the
renderer, JSON output, exit-code semantics, and all scorecard all come for
free.
Development #
dart pub get
dart analyze
dart test
test/fixtures/sample_project/ is a small, hand-built Dart project with
deliberate issues (a broken asset reference, an unused file, a missing
dependency, ...) that the test suite runs the real audits against — see
test/audits/*_test.dart and test/cli/*_test.dart. It's excluded from the
published package via .pubignore since its imports are intentionally fake.
Generate API docs locally with:
dart doc .