more_visibility 0.1.9
more_visibility: ^0.1.9 copied to clipboard
Analysis server plugin and builder bringing directory-scoped visibility via @mprotected and @mdefault annotations.
more_visibility #
Analysis server plugin + post-process builder that provides enhanced visibility control for Dart projects:
- Java-style "protected" and "default" visibility using
@mprotectedand@mdefaultannotations - Directory-private enforcement for underscore-prefixed directories (e.g.,
_components,_hooks)
Table of contents #
- Getting started
- Annotations
- Directory-private
- Configuring severity and disabling
- Ignoring the rule
- Auto-annotating generated files
- Example project
- Testing
What it does #
@mprotected: declaration/file is usable from the same directory and any subdirectories.@mdefault: declaration/file is usable only from the same directory.- Directory-private (automatic): underscore-prefixed directories like
_componentsor_hooksare only accessible from files at the same package depth.- Example:
lib/pages/_components/button.dartcan be imported bylib/pages/page.dartorlib/pages/_hooks/use_foo.dart, but NOT bylib/bar.dartorlib/pages/profile/page.dart.
- Example:
- Analysis rule powered by
analysis_server_plugincatches violations at analysis time in IDEs anddart analyze. - Post-process builder automatically stamps generated files (Riverpod, Freezed, etc.) with a file-level annotation so they obey the same visibility rules.
Requirements #
- Dart SDK 3.10.0 or later (Flutter SDK 3.38.0 or later)
Getting started #
- Add dependencies:
dependencies:
more_visibility_annotation: ^0.1.0
dev_dependencies:
more_visibility: ^0.1.3 # analysis server plugin
build_runner: any # if you want the auto-annotation builder
- Include the preset analysis options (enables the plugin, diagnostics on as errors by default):
include: package:more_visibility/more_visibility.yaml
If you already include another lint bundle, copy the plugin block from lib/all_visibility_rules.yaml into your own analysis_options.yaml.
Annotations #
Mark declarations or files to scope their visibility:
import 'package:more_visibility_annotation/more_visibility_annotation.dart';
@mprotected // usable from lib/ and lib/**
final shared = 1;
@mdefault // usable only inside this directory
final local = 2;
Directory-private #
Files in underscore-prefixed directories (like _components, _hooks, _utils) are automatically restricted to files at the same package depth. No annotations required — this rule is enforced automatically.
How it works #
A file inside a private directory can only be imported by files whose "package directory" (the path before any _* directory) matches.
Examples #
Project structure:
lib/
├── bar.dart
└── pages/
├── page.dart
├── _components/
│ └── button.dart
├── _hooks/
│ └── use_something.dart
└── profile/
└── page.dart
Allowed imports:
// lib/pages/page.dart
import '_components/button.dart'; // ✅ Same depth (lib/pages/)
// lib/pages/_hooks/use_something.dart
import '../_components/button.dart'; // ✅ Same depth (lib/pages/)
Blocked imports:
// lib/bar.dart
import 'pages/_components/button.dart'; // ❌ Different depth (lib/ vs lib/pages/)
// lib/pages/profile/page.dart
import '../_components/button.dart'; // ❌ Different depth (lib/pages/profile/ vs lib/pages/)
Error severity #
The directory_private rule defaults to error severity and will fail CI builds. Configure in analysis_options.yaml:
analyzer:
errors:
directory_private: warning # or info, or ignore
Configuring severity and disabling #
Visibility violations default to errors (configured in lib/more_visibility.yaml). Override per project:
analyzer:
errors:
# Change visibility violations to warnings
directory_private: warning
more_visibility_protected: warning
more_visibility_module_default: warning
# Or set to info (won't fail CI)
directory_private: info
more_visibility_protected: info
more_visibility_module_default: info
# Or ignore completely
directory_private: ignore
more_visibility_protected: ignore
more_visibility_module_default: ignore
plugins:
more_visibility:
diagnostics:
# Disable individual rules
directory_private: false
more_visibility_protected: false
more_visibility_module_default: false
Ignoring the rule #
Use analysis ignores when you need an escape hatch:
// ignore_for_file: directory_private, more_visibility_protected, more_visibility_module_default
// ignore: directory_private
import 'pages/_components/button.dart'; // bypass directory-private check
// ignore: more_visibility_protected
final value = exposedFromSibling;
File-level annotations #
Annotate an entire file to give every declaration the same visibility:
import 'package:more_visibility_annotation/more_visibility_annotation.dart';
@mprotected
library feature_auth;
// Everything in this file inherits the @mprotected rule.
Auto-annotating generated files #
Add the post-process builder so generated files copy the declaration-level visibility from their source part:
# build.yaml in your app/repo
targets:
$default:
builders:
more_visibility:auto_annotate:
enabled: true
post_process_builders:
more_visibility:auto_annotate:
options:
visibility: mprotected # fallback if source has no annotated declaration
Then run:
dart run build_runner build --delete-conflicting-outputs
The builder inserts @mprotected (or @mdefault) at the top of matching generated files (*.g.dart, *.freezed.dart, *.riverpod.dart) unless they are already annotated.
The builder copies the first declaration-level @mprotected/@mdefault from the source part file into the generated file (after part of), so generated declarations share the same visibility. File-level annotations are not copied because parts share library metadata automatically.
Example project #
See example/ for a minimal project showing allowed/blocked usages and how the lint reports violations.
Testing #
Run the package tests, which include an end-to-end lint invocation and builder coverage:
dart test