custom_lint

repository·main·Indexed 43 days ago

https://github.com/invertase/dart_custom_lint

A tool for building custom lint rules for Dart. It provides a simplified developer experience compared to the standard analyzer_plugin, featuring debugger support, hot-reload, and built-in testing mechanisms. The ecosystem includes custom_lint_builder for plugin authors, custom_lint_core for exporting lint utilities, and custom_lint_visitor for stable AST traversal decoupled from specific analyzer versions.

Tokens
6.4K
Snippets
23
Records
33
Agent score
56%

What's inside custom_lint

  1. What is custom_lint_builder and when to use it

    main

    The custom_lint_builder package is specifically designed for plugin authors who are creating custom lint plugins for the custom_lint framework.

    Important distinction:

    • Use custom_lint_builder if you are building a plugin that custom_lint should execute. Depending on this package signals to custom_lint that your package is a plugin.
    • Use custom_lint_core if you want to expose utilities or access classes like LintRule or Assist to other developers without making your package a runnable plugin itself.
  2. Understand the Astro Minimal project structure

    main

    The minimal template follows this directory structure:

    • public/: Contains static assets like images.
    • src/pages/: Contains .astro or .md files. Each file in this directory is exposed as a route based on its filename.
    • src/components/: A recommended location for Astro, React, Vue, Svelte, or Preact components.
    • package.json: Defines project dependencies and scripts.
    /
    ├── public/
    ├── src/
    │   └── pages/
    │       └── index.astro
    └── package.json
  3. Understand custom_lint_visitor versioning and analyzer compatibility

    main

    custom_lint_visitor uses a specific versioning scheme to track compatibility with the Dart analyzer package. The version format is major.minor.patch+analyzer_version.

    For example, version 1.0.0+6.7.0 indicates version 1.0.0 of the visitor, built for analyzer version 6.7.0.

    Dependency Management

    • General usage: Depending on custom_lint_visitor: ^1.0.0 allows you to support any compatible analyzer version within that range.
    • Strict requirements: If you need to require a specific analyzer version, you must specify the analyzer constraint explicitly in your pubspec.yaml.
  4. Use custom_lint_core to export lint utilities

    main

    custom_lint_core is a variant of custom_lint_builder designed for packages that want to export base classes for defining lint rules, fixes, or assists without being identified as a lint plugin themselves.

    When you add custom_lint_core as a dependency, custom_lint will not flag your package as a "custom_lint plugin". This makes it the correct choice for utility packages that provide building blocks for other lint plugins to use, rather than being the plugin itself.

  5. Debug custom lints with the Dart debugger

    main

    To debug your lint rules, you must enable debug mode in analysis_options.yaml and then attach your IDE's debugger to the VM service URI provided by custom_lint.

    1. Update analysis_options.yaml:
      custom_lint:
        debug: true
        verbose: true
    2. Obtain the VM service URI from the console output (if using custom_lint --watch) or from the custom_lint.log file created next to your pubspec.yaml.
    3. Use your IDE's 'Attach to Dart process' command and provide the URI (e.g., http://127.0.0.1:60671/9DS43lRMY90=/).
    custom_lint:
      debug: true
      verbose: true
  6. Test lints using // expect_lint

    main

    You can test your lint rules by creating files that contain code expected to trigger specific lints. Use the // expect_lint: <lint_code> comment immediately before the line of code being tested.

    • If the code correctly triggers the lint, the test passes.
    • If the code does NOT trigger the lint, the // expect_lint comment itself will trigger an error.

    Run custom_lint on your test/example folder to verify the results.

    // expect_lint: riverpod_final_provider
    var provider = Provider(...);
  7. Use a custom lint package in an application

    main

    To consume custom lints in a Dart or Flutter application:

    1. Add custom_lint and the specific lint package as dev_dependencies in your pubspec.yaml.
    2. Enable the custom_lint plugin in your analysis_options.yaml.
    3. Run pub get and restart your IDE.
    # analysis_options.yaml
    analyzer:
      plugins:
        - custom_lint
    # pubspec.yaml
    dev_dependencies:
      custom_lint:
      my_custom_lint_package:
  8. Create a custom lint package

    main

    To build a new linting package, you must define a plugin entrypoint and implement your lint rules using custom_lint_builder.

    1. Add custom_lint_builder and analyzer to your pubspec.yaml dependencies.
    2. Create a file at lib/<your_package_name>.dart.
    3. Implement createPlugin() which returns an instance of PluginBase.
    4. In your PluginBase implementation, override getLintRules to return a list of LintRule objects.
    5. Define your rules by extending DartLintRule and implementing the run method to inspect the AST via the CustomLintContext registry.
    # pubspec.yaml
    name: my_custom_lint_package
    environment:
      sdk: ">=3.0.0 <4.0.0"
    
    dependencies:
      analyzer:
      analyzer_plugin:
      custom_lint_builder:
    import 'package:analyzer/error/listener.dart';
    import 'package:custom_lint_builder/custom_lint_builder.dart';
    
    PluginBase createPlugin() => _ExampleLinter();
    
    class _ExampleLinter extends PluginBase {
      @override
      List<LintRule> getLintRules(CustomLintConfigs configs) => [
            MyCustomLintCode(),
          ];
    }
    
    class MyCustomLintCode extends DartLintRule {
      MyCustomLintCode() : super(code: _code);
    
      static const _code = LintCode(
        name: 'my_custom_lint_code',
        problemMessage: 'This is the description of our custom lint',
      );
    
      @override
      void run(
        CustomLintResolver resolver,
        ErrorReporter reporter,
        CustomLintContext context,
      ) {
        context.registry.addVariableDeclaration((node) {
          reporter.atNode(node, code);
        });
      }
    }
  9. Enable, disable, and configure lints

    main

    Custom lints are enabled by default. You can manage them in analysis_options.yaml under the custom_lint key.

    • Disable a specific rule: Set the rule name to false.
    • Disable all lints by default: Set enable_all_lint_rules: false and then explicitly list the rules you want to enable.
    • Configure a rule: Pass parameters to the rule within the rules list.
    # Disable a specific rule
    custom_lint:
      rules:
        - my_lint_rule: false
    
    # Disable all lints by default and only enable specific ones
    custom_lint:
      enable_all_lint_rules: false
      rules:
        - my_lint_rule
    
    # Configure a rule with parameters
    custom_lint:
      rules:
        - my_lint_rule:
            some_parameter: "some value"
  10. Use CustomLintContext to access project metadata and shared state

    main

    The CustomLintContext object is provided to your lint rule's run method and contains essential information for analysis:

    • registry: A LintRuleNodeRegistry used to add listeners to specific AstNodes. These listeners are executed after the run function completes.
    • sharedState: A Map<Object, Object?> shared across all lint rules, fixes, and assists running in the current session. This is useful for coordinating multiple rules.
    • pubspec: The Pubspec of the analyzed project. You can use this to enable or disable rules based on the presence of specific dependencies.
    • addPostRunCallback: A method to register a function that will be executed after all run (or Assist.run/Fix.run) methods have finished.