← 全部工具

kascote/dmetrics

热度 65 更新于 开发与构建

Static code metrics for Dart: cyclomatic and cognitive complexity per function, import coupling per library, a dependency graph view.

githubauto-collected

安装

暂未验证可直接使用的安装命令,请查看项目官方文档或 Release。

dmetrics

A Dart-native static code metrics engine. It measures every function-shaped scope in your code (functions, methods, getters, setters, operators, constructors, local functions, closures), compares each value against thresholds you configure, and prints one consolidated report as console text or JSON.

Three metrics ship today: cyclomatic complexity and cognitive complexity per function-shaped scope, and import coupling per library. The engine is built so that more metrics register without touching it.

Quick start

# Analyze the current directory
dart run bin/dmetrics.dart analyze

# Analyze specific files or directories, print every scope (not only problems)
dart run bin/dmetrics.dart analyze lib/src/config --all

# JSON report to stdout
dart run bin/dmetrics.dart analyze lib --json

Without any configuration every scope reports ok: there are no built-in thresholds. Values are measured and printed, but never judged until you set thresholds in analysisoptions.yaml or pass --threshold on the command line. See Configuration.

Reading the console output

One line per scope:

lib/src/config/loader.dart:372:1 • fail • function resolveRun • cyclomatic 22 [warn ≥ 10, fail ≥ 20] • loop ×8, if ×7, || ×1, && ×2, case ×2, ?? ×1

| Part | Meaning | | ------------------------ | --------------------------------------------------------------------------- | | path:line:col | Where the declaration starts, relative to the working directory. | | fail | Verdict: ok, warn, fail, or suppressed. | | function resolveRun | Scope kind and qualified name (Class.method, Class.method.<closure#1). | | cyclomatic 22 | Metric id and the value the verdict was computed on. | | [warn ≥ 10, fail ≥ 20] | The thresholds that applied. Absent when none is configured. | | loop ×8, if ×7, ... | Contributor summary: which constructs produced the score. | | if ×9 @2 | Most of those ifs sit two levels down (inside a loop inside an if, say). The suffix is the depth most occurrences of the kind share, ties to the deeper; absent when it is the top of the body. if ×9 is a run of guards, if ×9 @2 a nest. The JSON carries every contributor's depth. | | table-shaped: case | One kind supplies ≥ 70% of the score (and ≥ 8 in total): a dispatch table, a field-wise ==, a copyWith. Its size is the table's, not a tangle's. case pools pattern-or and when: a guarded arm is still one arm. For cognitive the family is a kind at a nesting level: table-shaped: if@1 means the score is mostly ifs one level down (a switch whose arms each hold a run of ifs), if alone means ifs at the top of the body. See below. | | cycle of 3 | On a library line: the library is in an import cycle of that many libraries of the run. The members are in the JSON detail. |

By default only warn, fail and suppressed scopes are printed, followed by a one-line summary. --all prints every scope. The summary ends with the run status (ok, violations, errors), which maps to the exit code.

Exit codes

| Exit | Status | Meaning | | ---- | ------------ | ------------------------------------------------------------------------------------------ | | 0 | ok | Analysis complete, no verdict at or above failon. | | 1 | violations | Analysis complete, at least one non-suppressed verdict at or above failon; with a baseline, one that is new or worse. | | 2 | errors | Analysis incomplete: parse errors, unreadable files, invalid config or baseline, conflicting settings. | | 3 | — | Usage error: bad flags, nonexistent target or --config file. |

Errors win over violations. Files with syntax errors are still measured from the recovered AST and marked partial, but the run is never clean. Config problems abort analysis entirely rather than measuring under a config you did not ask for.

Configuration

Config lives under a dmetrics: key in analysisoptions.yaml, next to the analyzer's own settings. Everything is optional. A bare dmetrics: with nothing under it is valid and just marks that directory as a config root with defaults.

dmetrics:
  fail_on: fail # run-global: warn | fail
  closure_rollup: separate # run-global: separate | include_in_parent
  include: ["lib/**", "bin/**"] # per-root discovery globs; default: every .dart file
  exclude: ["**.g.dart"] # per-root; default: ['**.g.dart', '**.freezed.dart']
  baseline: dmetrics_baseline.json # per-root; default: this name, used when present; `none` opts out
  metrics:
    cyclomatic:
      enabled: true
      thresholds: { warn: 10, fail: 20 } # the built-in default; `none` turns them off
      count_case_arms: true # run-global knob
      count_null_coalescing: true # run-global knob

thresholds: { warn: 15, fail: 25 } # the built-in default coupling: thresholds: { warn: 15, fail: 30 } # the built-in default overrides: # thresholds and enablement only; last match wins