shorthand_sanitizer 0.8.1
shorthand_sanitizer: ^0.8.1 copied to clipboard
Batch codemod for Dart 3.10 dot shorthands — rewrites Type.member to .member only where the analyzer proves it a no-op, prunes imports it orphans. Ships the dotsan CLI.
shorthand_sanitizer #
A safe, automated codemod for Dart 3.10+ dot shorthands. Rewrites Type.member to .member across your entire Flutter or Dart project, then automatically cleans up any imports orphaned by dropping the prefixes.
// Before
return Padding(
padding: EdgeInsets.all(16),
child: Text(
label,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
),
);
// After `dotsan && dart format .`
return Padding(
padding: .all(16),
child: Text(label, textAlign: .center, overflow: .ellipsis),
);
dotsan safely removes redundant type prefixes, and dart format naturally reflows arguments that now fit within your line length limit.
Installation #
AOT Native Binary (Default & Recommended) #
dart install compiles dotsan into a standalone native executable. Replaces Dart VM startup (~160 ms) with instant native execution (~20 ms):
dart install shorthand_sanitizer
Re-run it to upgrade; dart uninstall shorthand_sanitizer removes it. Ensure its bin directory is in your PATH (~/Library/Application Support/Dart/install/bin on macOS, ~/.local/state/Dart/install/bin on Linux — $XDG_STATE_HOME/Dart/install/bin if set — %LOCALAPPDATA%\Dart\install\bin on Windows).
Installed with the old recipe that compiled into
~/.pub-cache/bin? Runrm ~/.pub-cache/bin/dotsanfirst. Pub reads every file in its bin directory as a text stub, so a native binary there makes everydart pub global activate/deactivate— for any package — fail withFailed to decode data using encoding 'utf-8'.
Standard VM Installation #
If you prefer standard global activation without native compilation:
dart pub global activate shorthand_sanitizer
Ensure your pub cache bin directory is in your PATH (~/.pub-cache/bin on macOS/Linux, %LOCALAPPDATA%\Pub\Cache\bin on Windows).
Quick Start (Plug & Play) #
Run dotsan in any Dart or Flutter project root:
dotsan && dart format .
That's it! Your project is now upgraded to modern dot shorthands with zero orphaned imports.
Usage & Options #
# Sanitize all roots (lib, test, bin, etc.)
dotsan
# Preview changes (--dry-run)
dotsan lib test -n
# Keep specific members prefixed
dotsan --skip=AsyncValue.error
# Exclude matching file globs
dotsan --exclude="**/legacy/**"
# Also rewrite generated files
dotsan --include-generated
# Show version (-h for full options)
dotsan -v
--skip: AcceptsType.memberor baremembernames (comma-separated).--exclude: Glob pattern matching CWD-relative paths or file basenames (comma-separated).- Generated Files: Automatically detected and skipped by their header comment (e.g.,
build_runner,firebase_options.dart, pigeon, protoc, and slang outputs), while handwritten files likepage.preview.dartare processed normally.
How It Works & What Converts #
dotsan uses the Dart Analyzer API directly:
- Rewrites candidate expressions speculatively in memory.
- Re-resolves the AST in memory.
- Keeps a rewrite only if the shorthand resolves to the exact same element with zero new diagnostics or errors. If ambiguous or changed, it safely reverts.
- Prunes any
importdirectives left unused when prefixes are dropped.
Converts Cleanly #
| Kind | Before | After |
|---|---|---|
| Enum values | TextAlign.center |
.center |
| Named constructors | EdgeInsets.all(16) |
.all(16) |
| Factory constructors | BorderRadius.circular(8) |
.circular(8) |
| Static getters & fields | Duration.zero |
.zero |
| Const aliases | Alignment.topCenter |
.topCenter |
| Redirecting-factory forwarders | padding: EdgeInsets.only(left: 8) |
.only(left: 8) |
Intentionally Stays Prefixed #
dotsan leaves expressions prefixed when context type is ambiguous or would change program semantics:
// Unwitnessed context (type is Object, not Fit)
final Object o = Fit.cover;
// Sibling namespace (Colors.red, context is Color)
const Color c = Colors.red;
// Rebind risk (.a would silently bind Base.a)
const Base x = Sub.a;
// Context is List<Fit>, not enum
final l = Fit.values;
// Unnamed constructors (.new) are not rewritten
Text('Hello');
OS Caching & Performance #
Every candidate verification requires an analyzer resolution. To make runs fast, dotsan caches analyzer data on the OS:
- Cache Locations:
- macOS:
~/Library/Caches/dotsan - Linux:
$XDG_CACHE_HOME/dotsan(or~/.cache/dotsan) - Windows:
%LOCALAPPDATA%\dotsan
- macOS:
- What is cached: Linked element models for the Dart SDK, dependencies, and project libraries are persisted to an evicting file byte store (capped at 1 GiB, LRU) fronted by an in-memory cache.
- Effects on OS & Performance:
- First run vs subsequent runs: The first run links element models. Subsequent runs (such as running without
-nafter a--dry-run, or processing another project on the same SDK) skip linking and run >2x faster. - Content-addressed & safe: Cache entries are keyed by content signatures; stale entries never corrupt results.
- Safe to clear: You can wipe the cache folder at any time (
rm -rf ~/Library/Caches/dotsan);dotsanrebuilds it automatically on the next run.
- First run vs subsequent runs: The first run links element models. Subsequent runs (such as running without
Requirements #
- Target package language version ≥ 3.10 (packages below this are safely skipped as a clean no-op).
- Compatible with Dart & Flutter projects on macOS, Linux, and Windows.