doc: update documentations

This commit is contained in:
Sergey Penkovsky
2025-05-22 16:06:38 +03:00
parent 3bbecfb8ac
commit 0874cbe43a

View File

@@ -1,123 +1,167 @@
# cherrypick_generator
# Cherrypick Generator
[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)
A code generator for dependency injection (DI) modules in Dart, designed to work with [`cherrypick_annotations`](https://pub.dev/packages/cherrypick_annotations). This package generates efficient, boilerplate-free registration code for your annotated module classes—making the provisioning and resolution of dependencies fast, type-safe, and expressive.
**Cherrypick Generator** is a Dart code generation library for automatic boilerplate creation in dependency injection (DI) modules. It processes classes annotated with `@module()` (from [cherrypick_annotations](https://pub.dev/packages/cherrypick_annotations)) and generates code for registering dependencies, handling singletons, named bindings, runtime parameters, and more.
---
## Features
- **Generates DI module extension classes** from Dart classes annotated with `@module()`
- **Automatic binding** of methods and dependencies based on `@singleton()`, `@named('...')`, and parameter-level annotations
- **Optimized for readability/maintainability:** code output follows best practices for Dart and DI
- **Integrated with build_runner** for seamless incremental builds
- **Automatic Binding Generation:**
Generates `bind<Type>()` registration code for every method in a DI module marked with `@module()`.
- **Support for DI Annotations:**
Understands and processes meta-annotations such as `@singleton`, `@named`, `@instance`, `@provide`, and `@params`.
- **Runtime & Compile-Time Parameters:**
Handles both injected (compile-time) and runtime parameters for provider/binding methods.
- **Synchronous & Asynchronous Support:**
Correctly distinguishes between synchronous and asynchronous bindings (including `Future<T>` return types).
- **Named Bindings:**
Allows registration of named services via the `@named()` annotation.
- **Singletons:**
Registers singletons via the `@singleton` annotation.
---
## How It Works
1. You annotate your abstract classes with `@module()` and your provider methods inside with `@singleton()` or `@named()`.
2. Run `build_runner` to trigger code generation.
3. The generator creates a class (prefixed with `$`) extending your module, overriding the `builder()` method to register your dependencies with `bind<T>().toProvide(...)`, `.singleton()`, `.withName()`, etc.
1. **Annotations**
Annotate your module classes and methods using `@module()`, `@instance`, `@provide`, `@singleton`, and `@named` as needed.
**Example:**
2. **Code Scanning**
During the build process (with `build_runner`), the generator scans your annotated classes.
3. **Code Generation**
For each `@module()` class, a new class (with a `$` prefix) is generated.
This class overrides the `builder(Scope)` method to register all bindings.
4. **Binding Logic**
Each binding method's signature and annotations are analyzed. Registration code is generated according to:
- Return type (sync/async)
- Annotations (`@singleton`, `@named`, etc.)
- Parameter list (DI dependencies, `@named`, or `@params` for runtime values)
---
## Example
Given the following annotated Dart code:
```dart
import 'package:cherrypick_annotations/cherrypick_annotations.dart';
import 'package:cherrypick/cherrypick.dart';
part 'app_module.cherrypick.g.dart';
@module()
abstract class AppModule extends Module {
@singleton()
Dio dio() => Dio();
class MyModule {
@singleton
@instance
SomeService provideService(ApiClient client);
@named('apiBaseUrl')
String baseUrl() => 'https://api.example.com';
@provide
@named('special')
Future<Handler> createHandler(@params Map<String, dynamic> params);
}
```
Generates:
The generator will output (simplified):
```dart
final class $AppModule extends AppModule {
final class $MyModule extends MyModule {
@override
void builder(Scope currentScope) {
bind<Dio>().toProvide(() => dio()).singleton();
bind<String>().toProvide(() => baseUrl()).withName('apiBaseUrl');
bind<SomeService>()
.toInstance(provideService(currentScope.resolve<ApiClient>()))
.singleton();
bind<Handler>()
.toProvideAsyncWithParams((args) => createHandler(args))
.withName('special');
}
}
```
---
## Getting Started
## Generated Code Overview
### 1. Add dependencies
- **Constructor Registration:**
All non-abstract methods are considered as providers and processed for DI registration.
Add to your `pubspec.yaml`:
- **Parameter Handling:**
Each method parameter is analyzed:
- Standard DI dependency: resolved via `currentScope.resolve<Type>()`.
- Named dependency: resolved via `currentScope.resolve<Type>(named: 'name')`.
- Runtime parameter (`@params`): passed through as-is (e.g., `args`).
- **Binding Types:**
Supports both `.toInstance()` and `.toProvide()` (including async variants).
- **Singleton/Named:**
Appends `.singleton()` and/or `.withName('name')` as appropriate.
---
## Usage
1. **Add dependencies**
In your `pubspec.yaml`:
```yaml
dependencies:
cherrypick_annotations: ^latest
cherrypick_annotations: ^x.y.z
dev_dependencies:
cherrypick_generator: ^latest
build_runner: ^latest
build_runner: ^2.1.0
cherrypick_generator: ^x.y.z
```
### 2. Annotate modules
2. **Apply annotations**
Annotate your DI modules and provider methods as shown above.
See the example above. Use
- `@module()` on abstract classes
- `@singleton()` on methods for singleton bindings
- `@named('name')` on methods or method parameters to indicate named resolvers
### 3. Build
```shell
3. **Run the generator**
```
dart run build_runner build
# or with Flutter:
flutter pub run build_runner build
```
This generates `.cherrypick.g.dart` files containing the `$YourModule` classes.
4. **Import and use the generated code**
The generated files (suffix `.cherrypick.g.dart`) contain your `$YourModule` classes ready for use with your DI framework.
---
## Advanced Usage
## Advanced
**Parameter Injection with @named:**
- **Customizing Parameter Names:**
Use the `@named('value')` annotation on methods and parameters for named bindings.
```dart
@module()
abstract class NetworkModule {
@singleton()
Dio dio(@named('baseUrl') String url) => Dio(BaseOptions(baseUrl: url));
}
```
Which will be generated as:
- **Runtime Arguments:**
Use `@params` to designate parameters as runtime arguments that are supplied at injection time.
```dart
bind<Dio>().toProvide(() => dio(
currentScope.resolve<String>(named: 'baseUrl')
)).singleton();
```
- **Async Factories:**
Methods returning `Future<T>` generate the appropriate `.toProvideAsync()` or `.toInstanceAsync()` bindings.
---
## Developer Notes
- The generator relies on Dart's analyzer, source_gen, and build packages.
- Each class and method is parsed for annotations; missing required annotations (like `@instance` or `@provide`) will result in a generation error.
- The generated code is designed to extend your original module classes while injecting all binding logic.
---
## License
Licensed under the [Apache License 2.0](LICENSE).
```
Licensed under the Apache License, Version 2.0
```
---
## Contributing
## Contribution
PRs and issues welcome! Please file bugs or feature requests via GitHub.
Pull requests and issues are welcome! Please open git issues or submit improvements as needed.
---
## Author
Sergey Penkovsky (<sergey.penkovsky@gmail.com>)