Custom Lint Library
A Dart/Flutter library providing custom lint rules for better code quality and testing practices.
Project Structure
lib/
core/ # Core framework and base classes
analyzers/ # All analyzer implementations
base_analyzer.dart
prefer_fake_over_mock_analyzer.dart
no_optional_operators_in_tests_analyzer.dart
...more analyzers
base_lint_rule.dart # Base class for all lint rules
models/
lint_issue.dart # Data model for lint issues
custom_lint_rules/ # All lint rule implementations
prefer_fake_over_mock_rule.dart
no_optional_operators_in_tests.dart
...more rules
ripplearc_linter.dart # Main plugin entry point
custom_lint_package.dart # Package configuration
test/
custom_lint_rules/ # All rule tests go here
prefer_fake_over_mock_rule_test.dart
no_optional_operators_in_tests_test.dart
...more tests
utils/ # Test utilities
custom_lint_resolver.dart
test_error_reporter.dart
example/ # Example files demonstrating rules
example_prefer_fake_over_mock_rule.dart
example_no_optional_operators_in_tests_rule.dart
...more examples
Registering a Custom Lint Rule
To register a custom lint rule in your package, follow these steps:
-
Create the Analyzer: Implement your analyzer by extending
BaseAnalyzerinlib/core/analyzers/. For example:class ForcedUnwrappingAnalyzer extends BaseAnalyzer { @override String get ruleName => 'forbid_forced_unwrapping'; @override String get problemMessage => 'Forced unwrapping (!) is not allowed in production code.'; @override String get correctionMessage => 'Use null-safe alternatives like null coalescing (??) or explicit null checks.'; @override List<LintIssue> analyze(CompilationUnit node) { final issues = <LintIssue>[]; // Implement your analysis logic here return issues; } } -
Create the Lint Rule: Implement your lint rule by extending
BaseLintRuleinlib/custom_lint_rules/. For example:import '../core/base_lint_rule.dart'; import '../core/analyzers/forced_unwrapping_analyzer.dart'; import '../core/analyzers/base_analyzer.dart'; class ForbidForcedUnwrapping extends BaseLintRule { ForbidForcedUnwrapping() : super(BaseLintRule.createLintCode(_analyzer)); static final _analyzer = ForcedUnwrappingAnalyzer(); @override BaseAnalyzer get analyzer => _analyzer; } -
Write Unit Tests: Create unit tests in
test/custom_lint_rules/to verify your rule works as expected:void main() { group('ForbidForcedUnwrapping', () { late ForbidForcedUnwrapping rule; late TestErrorReporter reporter; setUp(() { rule = ForbidForcedUnwrapping(); reporter = TestErrorReporter(); }); test('should flag forced unwrapping in production code', () async { const source = ''' void main() { final String? name = null; final value = name!; // Should flag this print(value); } '''; await analyzeCode(source, path: 'lib/example.dart'); expect(reporter.errors, hasLength(1)); expect(reporter.errors.first.errorCode.name, equals('forbid_forced_unwrapping')); }); }); } -
Create an Example File: Create an example in
example/that demonstrates both the violation and correct usage:class User { final String? name; User({this.name}); } void main() { final user = User(name: null); // Bad: Using forced unwrapping final name = user.name!; // LINT print('User: $name'); // Will crash at runtime // Good: Using null-safe alternatives final safeName = user.name ?? 'Unknown'; print('User: $safeName'); // Safe, will print "User: Unknown" } -
Register the Rule: In
lib/ripplearc_linter.dart, add your rule to the list:class _RipplearcFlutterLint extends PluginBase { @override List<LintRule> getLintRules(CustomLintConfigs configs) => [ ForbidForcedUnwrapping(), // ... other rules ]; } -
Configure the Linter: Copy the existing configuration from
example/custom_lint.yamlto your project root:cp example/custom_lint.yaml custom_lint.yaml -
Run the Linter: Use
dart run custom_lintto verify your rule works as expected.
By following these steps, you can successfully register and use custom lint rules in your Dart/Flutter project.
Configuration Files
analysis_options.yaml
This file configures the Dart analyzer and enables the custom lint plugin. Place it in your project root:
analyzer:
plugins:
- custom_lint # Enables the custom_lint plugin
custom_lint.yaml
This configuration file includes all our custom lint rules:
avoid_static_colors- Enforce theme-based colors instead of static color usageavoid_static_typography- Enforce theme-based typography instead of static typography or raw TextStyle/GoogleFonts usageprefer_fake_over_mock- Prefer using Fake over Mock for test doublesforbid_forced_unwrapping- Forbid forced unwrapping in production codeforbid_modular_get_outside_module- ForbidModular.get<T>()outside*_module.dartfiles; enforce constructor injectionno_optional_operators_in_tests- Forbid optional operators in test filesno_direct_instantiation- Enforce dependency injection by forbidding direct class instantiationdocument_fake_parameters- Enforce documentation on Fake classes and their non-private memberstodo_with_story_links- Ensure TODO comments include YouTrack story linksno_internal_method_docs- Forbid documentation on private methods to reduce noisedocument_interface- Enforce documentation on abstract classes and their public methodsprevent_feature_module_dependencies- Enforce feature module independence by preventing features from depending on other featuresprevent_library_module_dependencies- Enforce library module independence by preventing libraries from depending on features
Rule Configuration
- Each rule is listed under the
rulessection - Rules are enabled by default when listed
- The order of rules doesn't matter
- All rules from the library are available to use
Plugin Configuration
- The
analyzer.pluginssection must includecustom_lint_library - This enables our custom lint rules to be loaded
- Multiple plugins can be listed if needed
By following these steps, you can successfully register and use custom lint rules in your Dart/Flutter project.
Publishing & Updating the Package
To update and publish a new version to pub.dev:
- Add notes to the changelog
- Edit
CHANGELOG.mdand add a section for the new version, describing the changes and new rules.
- Edit
- Run a dry run
- Execute
dart pub publish --dry-runto check for issues or warnings before publishing.
- Execute
- Fix any issues
- Address any errors or important warnings reported by the dry run (e.g., dependency constraints, missing files, linter errors).
- Publish
- Update the version in
pubspec.yaml. - Run
dart pub publishand follow the prompts to publish your package to pub.dev.
- Update the version in
Note: You cannot re-publish the same version. Always increment the version number for each release.
Using the Latest Lint Rules in Your Project
To update your project to use the latest version of ripplearc_linter and enable new rules:
-
Update your pubspec.yaml
- Change the version of
ripplearc_linterto the latest version:dependencies: ripplearc_linter: ^<latest_version> - Run
dart pub getto fetch the updated package.
- Change the version of
-
Update your custom_lint.yaml
- Add or update the rules you want to enforce. For example:
rules: - avoid_static_colors - prefer_fake_over_mock - forbid_forced_unwrapping - forbid_modular_get_outside_module - no_optional_operators_in_tests - no_direct_instantiation - document_fake_parameters - document_interface - todo_with_story_links - no_internal_method_docs - prevent_feature_module_dependencies - prevent_library_module_dependencies - Only include the rules you want to enforce in your project.
- Add or update the rules you want to enforce. For example:
Tip: After updating, run your linter to ensure the new rules are active and working as expected.
Testing Integration
To test the integration of this custom lint library in your project, you can point to a specific branch in your pubspec.yaml:
dependencies:
ripplearc_linter:
git:
url: https://github.com/ripplearc/ripplearc-flutter-lint.git
ref: chore/no-direct-instantiation-exception
This allows you to test changes from a specific branch before they are published to pub.dev. Replace chore/no-direct-instantiation-exception with the branch name you want to test.
Libraries
- core/analyzers/avoid_static_colors_analyzer
- core/analyzers/avoid_static_typography_analyzer
- core/analyzers/avoid_test_timeouts_analyzer
- core/analyzers/base_analyzer
- core/analyzers/direct_instantiation_analyzer
- core/analyzers/direct_instantiation_helpers/config_parser
- core/analyzers/direct_instantiation_helpers/context_checker
- core/analyzers/direct_instantiation_helpers/import_checker
- core/analyzers/direct_instantiation_helpers/linter_config
- core/analyzers/direct_instantiation_helpers/package_checker
- core/analyzers/direct_instantiation_helpers/type_checker
- core/analyzers/direct_instantiation_helpers/visitor
- core/analyzers/document_enum_analyzer
- core/analyzers/document_fake_parameters_analyzer
- core/analyzers/document_interface_analyzer
- core/analyzers/forbid_datetime_now_analyzer
- core/analyzers/forbid_helper_util_naming_analyzer
- core/analyzers/forbid_modular_get_outside_module_analyzer
- core/analyzers/forced_unwrapping_analyzer
- core/analyzers/no_internal_method_docs_analyzer
- core/analyzers/no_optional_operators_in_tests_analyzer
- core/analyzers/prefer_fake_over_mock_analyzer
- core/analyzers/prevent_feature_module_dependencies_analyzer
- core/analyzers/prevent_library_module_dependencies_analyzer
- core/analyzers/private_subject_analyzer
- core/analyzers/restrict_core_icon_data_analyzer
- core/analyzers/sealed_over_dynamic_analyzer
- core/analyzers/specific_exception_types_analyzer
- core/analyzers/test_file_mutation_coverage_analyzer
- core/analyzers/todo_with_story_links_analyzer
- core/base_lint_rule
- core/models/lint_issue
- core/utils/documentation_utils
- core/utils/feature_path_utils
- custom_lint_package
- custom_lint_rules/avoid_static_colors
- custom_lint_rules/avoid_static_typography
- custom_lint_rules/avoid_test_timeouts
- custom_lint_rules/document_enum
- custom_lint_rules/document_fake_parameters
- custom_lint_rules/document_interface
- custom_lint_rules/forbid_datetime_now
- custom_lint_rules/forbid_forced_unwrapping
- custom_lint_rules/forbid_helper_util_naming
- custom_lint_rules/forbid_modular_get_outside_module
- custom_lint_rules/no_direct_instantiation
- custom_lint_rules/no_internal_method_docs
- custom_lint_rules/no_optional_operators_in_tests
- custom_lint_rules/prefer_fake_over_mock_rule
- custom_lint_rules/prevent_feature_module_dependencies
- custom_lint_rules/prevent_library_module_dependencies
- custom_lint_rules/private_subject
- custom_lint_rules/restrict_core_icon_data
- custom_lint_rules/sealed_over_dynamic
- custom_lint_rules/specific_exception_types
- custom_lint_rules/test_file_mutation_coverage
- custom_lint_rules/todo_with_story_links
- ripplearc_linter