shorthand_sanitizer 0.8.1 copy "shorthand_sanitizer: ^0.8.1" to clipboard
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 #

pub package Dart SDK

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 #

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? Run rm ~/.pub-cache/bin/dotsan first. Pub reads every file in its bin directory as a text stub, so a native binary there makes every dart pub global activate/deactivate — for any package — fail with Failed 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: Accepts Type.member or bare member names (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 like page.preview.dart are processed normally.

How It Works & What Converts #

dotsan uses the Dart Analyzer API directly:

  1. Rewrites candidate expressions speculatively in memory.
  2. Re-resolves the AST in memory.
  3. 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.
  4. Prunes any import directives 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
  • 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 -n after 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); dotsan rebuilds it automatically on the next run.

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.
2
likes
150
points
275
downloads

Documentation

API reference

Publisher

verified publishermehmetesen.com

Weekly Downloads

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.

Homepage
Repository (GitHub)
View/report issues

Topics

#analyzer #code-style #static-analysis #cli #developer-tools

License

MIT (license)

Dependencies

analyzer, args, cli_util, glob, path

More

Packages that depend on shorthand_sanitizer