# custom_lint 0.3.3

Lint rules are a powerful way to improve the maintainability of a project. Custom Lint allows package authors and developers to easily write custom lint rules.




## Installing
```
export PUB_HOSTED_URL=https://pub.mirror.p-hatan.ir
flutter pub add 'custom_lint:0.3.3'
```
Published: 2023-04-06T14:57:02.113259Z
Expected archive SHA-256: 80ccf10b30d4f1d0b40bd6d276a86d72266569c0a7542f1b839daf2fd05c16a5

Available in cache; verification: upstream_checksum
## Direct dependencies
- analyzer: ^5.7.0
- analyzer_plugin: ^0.11.0
- args: ^2.3.1
- async: ^2.8.2
- ci: ^0.1.0
- cli_util: ^0.4.0
- collection: ^1.16.0
- freezed_annotation: ^2.2.0
- json_annotation: ^4.7.0
- meta: ^1.7.0
- package_config: ^2.0.2
- path: ^1.8.0
- pub_semver: ^2.1.1
- pubspec_parse: ^1.2.0
- rxdart: ^0.27.7
- uuid: ^3.0.6
- yaml: ^3.1.1

## Package README (untrusted upstream documentation)
<p align="center">
  <h1>custom_lint</h1>
  <span>Tools for building custom lint rules.</span>
</p>

<p align="center">
  <a href="https://github.com/invertase/dart_custom_lint/blob/main/LICENSE">License</a>
</p>

---

 <a href="https://invertase.link/discord">
   <img src="https://img.shields.io/discord/295953187817521152.svg?style=flat-square&colorA=7289da&label=Chat%20on%20Discord" alt="Chat on Discord">
 </a>

---

## Index

- [Index](#index)
- [Tutorial](#tutorial)
- [About](#about)
- [Usage](#usage)
  - [Creating a custom lint package](#creating-a-custom-lint-package)
  - [Using our custom lint package in an application](#using-our-custom-lint-package-in-an-application)
  - [Obtaining the list of lints in the CI](#obtaining-the-list-of-lints-in-the-ci)
  - [Using the Dart debugger](#using-the-dart-debugger)
  - [Testing your plugins using expect\_lint](#testing-your-plugins-using-expect_lint)

## Tutorial

You can read the latest [blog post](https://invertase.link/b18R) or watch the [advanced use case with custom_lint video](https://invertase.link/RNoz).

## About

Lint rules are a powerful way to improve the maintainability of a project.
The more, the merrier!
But while Dart offers a wide variety of lint rules by default, it cannot
reasonably include every possible lint. For example, Dart does not
include lints related to third-party packages.

Custom_lint fixes that by allowing package authors to write custom lint rules.

Custom_lint is similar to [analyzer_plugin], but goes deeper by trying to
provide a better developer experience.

That includes:

- A command-line to obtain the list of lints in your CI
  without having to write a command line yourself.
- A simplified project setup:
  No need to deal with the `analyzer` server or error handling. Custom_lint
  takes care of that for you, so that you can focus on writing lints.
- Debugger support.
  Inspect your lints using the Dart debugger and place breakpoints.
- Supports hot-reload/hot-restart:
  Updating the source code of a linter plugin will dynamically restart it,
  without having to restart your IDE/analyzer server.
- Built-in support for `// ignore:` and `// ignore_for_file:`.
- Built-in testing mechanism using `// expect_lint`. See [Testing your plugins using expect_lint](#testing-your-plugins-using-expect_lint)
- Support for `print(...)` and exceptions:
  If your plugin somehow throws or print debug messages, custom_lint
  will generate a log file with the messages/errors.

## Usage

Using custom_lint is split in two parts:

- how to define a custom_lint package
- how users can install our package in their application to see our newly defined lints

### Creating a custom lint package

To create a custom lint, you will need two things:

- updating your `pubspec.yaml` to include `custom_lint_builder` as a dependency:

  ```yaml
  # pubspec.yaml
  name: my_custom_lint_package
  environment:
    sdk: ">=2.16.0 <3.0.0"

  dependencies:
    # we will use analyzer for inspecting Dart files
    analyzer:
    analyzer_plugin:
    # custom_lint_builder will give us tools for writing lints
    custom_lint_builder:
  ```

- create a `lib/<my_package_name>.dart` file in your project with the following:

  ```dart
  // This is the entrypoint of our custom linter
  PluginBase createPlugin() => _ExampleLinter();

  /// A plugin class is used to list all the assists/lints defined by a plugin.
  class _ExampleLinter extends PluginBase {
    /// We list all the custom warnings/infos/errors
    @override
    List<LintRule> getLintRules(CustomLintConfigs configs) => [
          MyCustomLintCode(),
        ];
  }

  class MyCustomLintCode extends DartLintRule {
    MyCustomLintCode() : super(code: _code);

    /// Metadata about the warning that will show-up in the IDE.
    /// This is used for `// ignore: code` and enabling/disabling the lint
    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,
    ) {
      // Our lint will highlight all variable declarations with our custom warning.
      context.registry.addVariableDeclaration((node) {
        // "node" exposes metadata about the variable declaration. We could
        // check "node" to show the lint only in some conditions.

        // This line tells custom_lint to render a waring at the location of "node".
        // And the warning shown will use our `code` variable defined above as description.
        reporter.reportErrorForNode(code, node);
      });
    }
  }
  ```

That's it for defining a custom lint package!

If you're looking for a more advanced example, see the [example](https://github.com/invertase/dart_custom_lint/tree/main/packages/custom_lint/example).
This example implements:

- a lint appearing on all variables of a specific type
- a quick fix for that lint
- an "assist" for providing refactoring options.

Let's now use it in an application.

### Using our custom lint package in an application

For users to run custom_lint packages, there are a few steps:

- The application must contain an `analysis_options.yaml` with the following:

  ```yaml
  analyzer:
    plugins:
      - custom_lint
  ```

- The application also needs to add `custom_lint` and our package(s) as dev
  dependency in their application:

  ```yaml
  # The pubspec.yaml of an application using our lints
  name: example_app
  environment:
    sdk: ">=2.16.0 <3.0.0"

  dev_dependencies:
    custom_lint:
    my_custom_lint_package:
  ```

That's all!
After running `pub get` (and possibly restarting their IDE), users should now
see our custom lints in their Dart files:

![screenshot of our custom lints in the IDE](https://raw.githubusercontent.com/invertase/dart_custom_lint/main/resources/lint_showcase.png)

### Obtaining the list of lints in the CI

Unfortunately, running `dart analyze` does not pick up our newly defined lints.
We need a separate command for this.

To do that, users of our custom lint package can run the following inside their terminal:

```sh
$ dart run custom_lint
  lib/main.dart:0:0 • This is the description of our custom lint • my_custom_lint_code
```

If you are working on a Flutter project, run `flutter pub run custom_lint` instead.

### Using the Dart debugger

To debug plugins in custom_lint, you need to connect to plugins using "attach"
mode in your IDE (`cmd+shift+p` + `Debug: attach to Dart process` in VSCode).

When using this command, you will need a VM service URI provided by custom_lint.

There are two possible ways to obtain one:

- if you started your plugin using `custom_lint --watch`, it should be visible
  in the console output.
- if your plugin is started by your IDE, you can open the `custom_lint.log` file
  that custom_lint created next to the `pubspec.yaml` of your analyzed projects.

In both cases, what you're looking for is logs similar to:

```
The Dart VM service is listening on http://127.0.0.1:60671/9DS43lRMY90=/
The Dart DevTools debugger and profiler is available at: http://127.0.0.1:60671/9DS43lRMY90=/devtools/#/?uri=ws%3A%2F%2F127.0.0.1%3A60671%2F9DS43lRMY90%3D%2Fws
```

What you'll want is the first URI. In this example, that is `http://127.0.0.1:60671/9DS43lRMY90=/`.
You can then pass this to your IDE, which should now be able to attach to the
plugin.

### Testing your plugins using expect_lint

Custom_lint comes with an official testing mechanism for asserting that your
plugins correctly work.

Testing your plugins is straightforward: Simply write a file that should contain
lints from your plugin (such as the example folder). Then, using a syntax
similar to `// ignore`, write a `// expect_lint: code` in the line before
your lint:

```dart
// expect_lint: riverpod_final_provider
var provider = Provider(...);
```

When doing this, there are two possible cases:

- The line after the `expect_lint` correctly contains the expected lint.
  In that case, the lint is ignored (similarly to if we used `// ignore`)
- The next line does **not** contain the lint.
  In that case, the `expect_lint` comment will have an error.

This allows testing your plugins by simply running `custom_lint` on your test/example folder.
Then, if any expected lint is missing, the command will fail. But if your plugin correctly
emits the lint, the command will succeed.

---

<p align="center">
  <a href="https://invertase.io/?utm_source=readme&utm_medium=footer&utm_campaign=dart_custom_lint">
    <img width="75px" src="https://static.invertase.io/assets/invertase/invertase-rounded-avatar.png">
  </a>
  <p align="center">
    Built and maintained by <a href="https://invertase.io/?utm_source=readme&utm_medium=footer&utm_campaign=dart_custom_lint">Invertase</a>.
  </p>
</p>

[analyzer_plugin]: https://pub.dev/packages/analyzer_plugin

## Changelog
## 0.3.3 - 2023-04-06

- Reduce the likelyness of a dependency version conflict.
- Fix `dart analyze` crashing on large projects in the CI due to custom_lint
  incorrectly trying to run plugins in debug mode.
- Fix the `custom_lint` command line never terminating in some cases where plugins
  fail to start (thanks to @kuhnroyal).
- Upgraded `analyzer` to `>=5.7.0 <5.11.0`
- `LintRuleNodeRegistry` and other AstVisitor-like now are based off `GeneralizingAstVisitor` instead of `GeneralizingAstVisitor`
- Upgraded `cli_util` to `^0.4.0`
- The command line no-longer throws if ran on an empty project or a project with
  no plugins enabled
- Exposes the Pubspec in CustomLintContext

## 0.3.2 - 2023-03-09

- Revert "Fixed an issue that caused a "Port already in use" error when
  trying to start custom_lint".
  This had the opposite effect of what's expected.

## 0.3.0 - 2023-03-09

- Update analyzer to >=5.7.0 <5.8.0
- Fixed an issue that caused a "Port already in use" error when trying to
  start custom_lint

## 0.2.12

Move json_serializable to dev dependencies

## 0.2.11

- Improved the error message when there is a version conflict in mono-repos (thanks to @@adsonpleal)
- Bump minimum Dart SDK to `sdk: ">=2.19.0 <3.0.0"`

## 0.2.5

Fix custom_lint not correctly killing sub-processes when the IDE stops custom_lint.

## 0.2.2

Fixes an exception thrown when a project contains images.

## 0.2.0

**Large Breaking change**
This new version introduces large changes to how lints/fixes/assists are defined.  
Long story short, besides the `createPlugin` method, the entire syntax changed.

See the readme, examples, and docs around how to use the new syntax.

The new syntax has multiple benefits:

- It is now possible to enable/disable lints inside the `analysis_options.yaml`
  as followed:

  ```yaml
  # optional
  include: path/to/another/analysis_options.yaml

  custom_lint:
    rules:
      # enable a lint rule
      - my_lint_rule
      # A lint rule that is explicitly disabled
      - another_lint_rule: false
  ```

  Enabling/disabling lints is supported by default with the new syntax. Nothing to do~

- Performance improvement when using a large number of lints.
  The workload of analyzing files is now shared between lints.

- The new syntax makes the code simpler to maintain.
  Before, the `PluginBase.getLints` rapidly ended-up doing too much.
  Now, it is simple to split the implementation in multiple bits

## 0.1.2-dev

Do some internal refactoring as an attempt to fix #60

## 0.1.1

- Fix an issue where plugins were hot-reloaded when the file analyzed changed.
- Optimized analysis such that `PluginBase.getLints()` is theorically not reinvoked
  unless the file analyzed changed.

## 0.1.0

- **Breaking**: The plugin entrypoint has moved.  
  Plugins no-longer should define a `/bin/custom_lint.dart` file.
  Instead they should define a `/lib/<my_package_name>.dart`

- **Breaking**: The plugin entrypoint is modified. Plugins no-longer
  define a "main", but instead define a `createPlugin` function:

  Before:

  ```dart
  // /bin/custom_lint.dart
  void main(List<String> args, SendPort sendPort) {
    startPlugin(sendPort, MyPlugin());
  }
  ```

  After:

  ```dart
  // /lib/<my_package_name.dart
  MyPlugin createPlugin() => MyPlugin();
  ```

- Add assist support.
  Inside your plugins, you can now override `handleGetAssists`:

  ```dart
  import 'package:analyzer_plugin/protocol/protocol_generated.dart'
    as analyzer_plugin;

  class MyPlugin extends PluginBase {
    // ...

    Future<analyzer_plugin.EditGetAssistsResult> handleGetAssists(
      ResolvedUnitResult resolvedUnitResult, {
      required int offset,
      required int length,
    }) async {
        // TODO return some assists for the given offset
    }
  }
  ```

## 0.0.16

Fix `expect_lint` not working if the file doesn't contain any lint.

## 0.0.15

- Custom_lint now has a built-in mechanism for testing lints.
  Simply write a file that should contain lints for your plugin.
  Then, using a syntax similar to `// ignore`, write a `// expect_lint: code`
  in the line before your lint:

  ```dart
  // expect_lint: riverpod_final_provider
  var provider = Provider(...);
  ```

  When doing this, there are two possible cases:

  - The line after the `expect_lint` correctly contains the expected lint.  
    In that case, the lint is ignored (similarly to if we used `// ignore`)
  - The next line does **not** contain the lint.
    In that case, the `expect_lint` comment will have an error.

  This allows testing your plugins by simply running `custom_lint` on your test/example folder.
  Then, if any expected lint is missing, the command will fail. But if your plugin correctly
  emits the lint, the command will succeed.

- Upgrade analyzer/analzer_plugin

## 0.0.14

- Fix custom_lint not working in the IDE

## 0.0.13

- Add debugger and hot-reload support (Thanks to @TimWhiting)
- Correctly respect `exclude` obtains from the analysis_options.yaml
- Fix `dart analyze` incorrectly failing due to showing the "plugin is starting" lint.

## 0.0.12

- Fix custom_lint plugins not working in release mode and when using git dependencies (thanks to @TimWhiting)
- Fix command line exit code not being set properly (thansk to @andrzejchm)

## 0.0.11

Fix custom_lint not showing in the IDE

## 0.0.10+1

Update docs

## 0.0.10

- Upgrade Riverpod to 2.0.0
- Fix deprecation errors with analyzer

## 0.0.9+1

Update description and readme

## 0.0.9

- Lint fixes can now be used when placing the cursor on the last character of a lint
- improve pub score

## 0.0.8

Allow lints to emit fixes

## 0.0.7

Fix a bug where the custom_lint command line may not list all lints

## 0.0.6

feat!: getLints now is expected to return a `Stream<Lint>` instead of `Iterable<Lint>`

fix: a bug where the lints shown by the IDE could get out of sync with the actual content of the file

## 0.0.5

Fixed error reporting if a custom_lint plugin throws but the exception comes
from a package instead of the plugin itself.

## 0.0.4

- Fixed a bug where the command line could show IDE-only meant for debugging

## 0.0.3

PluginBase.getLints now receive a `ResolvedUnitResult` instead of a `LibraryElement`.

## 0.0.2

- Compilation errors are now visible within the `pubspec.yaml` of applications
  that are using the plugin.

- Plugins that are currently loading are now highlighted inside the `pubspec.yaml`
  of applications that are using the plugin.

- If a plugin throws when trying to analyze a Dart file, the IDE will now
  show the exception at the top of the analyzed file.

- Compilation errors, exceptions and prints are now accessible within
  a log file (`custom_lint.log`) inside applications using the plugin.

## 0.0.1

Initial release







 
