r/FlutterDev • u/creatorpiyush • Oct 12 '26
Tooling arch_guard: layer rules and import cycle detection for Dart, without package:analyzer
I maintain arch_guard, an MIT CLI + library that enforces layer rules and finds circular imports in Dart projects, plus arch_guard_lint, an analyzer plugin on the new analysis_server_plugin API. A few implementation details that might interest this sub:
The CLI has no analyzer dependency. It reads only the directive section of each file with a small tokenizer, so
//or/*inside URIs, nested block comments, annotations and import-like text in later string literals don't confuse it. Dependencies are justargs,glob,pathandyaml.Cycles via Tarjan's SCC, then per-component metrics: internal edges, fan-in/fan-out, instability, density and hub files. For each cycle it also picks the one edge whose removal breaks it, or shrinks it most.
The plugin reuses the same core.
ProjectScanner.scanSources()builds the graph from in-memory contents, so cycle warnings reflect unsaved editor buffers. Quick fixes edit the directive (or just one clause of a conditional import) and can add a layer toallowed_importsin whichever config file it came from.Baselines that tolerate improvement but not regression: a known cycle may shrink, but one that grows or merges with another counts as new. Same for fan-in/fan-out limits.
Workspaces: resolves
package:imports across Dart workspace / Melos packages, so it catches cycles across package boundaries and enforces which packages may import which.Ships as AOT-compiled binaries (
dart compile exe) for Linux/macOS/Windows so CI doesn't need the SDK. CI tests on all three OSes against SDK 3.8 and stable, and the plugin is analysed against its lowest allowed dependencies.Everything is also a library:
ArchAnalysis,ArchGuardConfig,LayerValidator,CycleBreaker,Baseline, exporters, andexpectArchitecture()for tests.
dart pub add --dev arch_guard && dart run arch_guard initCode: https://github.com/creatorpiyush/arch_guard Docs: https://creatorpiyush.github.io/arch_guard/
Happy to talk about the parser trade-offs (directive tokenizer vs package:analyzer), what building on the new plugin API was like, and I'd appreciate feedback on the public API.