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) **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.
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.
--- ---
## Features ## Features
- **Generates DI module extension classes** from Dart classes annotated with `@module()` - **Automatic Binding Generation:**
- **Automatic binding** of methods and dependencies based on `@singleton()`, `@named('...')`, and parameter-level annotations Generates `bind<Type>()` registration code for every method in a DI module marked with `@module()`.
- **Optimized for readability/maintainability:** code output follows best practices for Dart and DI
- **Integrated with build_runner** for seamless incremental builds - **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 ## How It Works
1. You annotate your abstract classes with `@module()` and your provider methods inside with `@singleton()` or `@named()`. 1. **Annotations**
2. Run `build_runner` to trigger code generation. Annotate your module classes and methods using `@module()`, `@instance`, `@provide`, `@singleton`, and `@named` as needed.
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.
**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 ```dart
import 'package:cherrypick_annotations/cherrypick_annotations.dart'; import 'package:cherrypick_annotations/cherrypick_annotations.dart';
import 'package:cherrypick/cherrypick.dart';
part 'app_module.cherrypick.g.dart';
@module() @module()
abstract class AppModule extends Module { class MyModule {
@singleton() @singleton
Dio dio() => Dio(); @instance
SomeService provideService(ApiClient client);
@named('apiBaseUrl') @provide
String baseUrl() => 'https://api.example.com'; @named('special')
Future<Handler> createHandler(@params Map<String, dynamic> params);
} }
``` ```
Generates: The generator will output (simplified):
```dart ```dart
final class $AppModule extends AppModule { final class $MyModule extends MyModule {
@override @override
void builder(Scope currentScope) { void builder(Scope currentScope) {
bind<Dio>().toProvide(() => dio()).singleton(); bind<SomeService>()
bind<String>().toProvide(() => baseUrl()).withName('apiBaseUrl'); .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`).
```yaml - **Binding Types:**
dependencies: Supports both `.toInstance()` and `.toProvide()` (including async variants).
cherrypick_annotations: ^latest
dev_dependencies: - **Singleton/Named:**
cherrypick_generator: ^latest Appends `.singleton()` and/or `.withName('name')` as appropriate.
build_runner: ^latest
```
### 2. Annotate modules
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
dart run build_runner build
```
This generates `.cherrypick.g.dart` files containing the `$YourModule` classes.
--- ---
## Advanced Usage ## Usage
**Parameter Injection with @named:** 1. **Add dependencies**
In your `pubspec.yaml`:
```yaml
dependencies:
cherrypick_annotations: ^x.y.z
```dart dev_dependencies:
@module() build_runner: ^2.1.0
abstract class NetworkModule { cherrypick_generator: ^x.y.z
@singleton() ```
Dio dio(@named('baseUrl') String url) => Dio(BaseOptions(baseUrl: url));
}
```
Which will be generated as:
```dart 2. **Apply annotations**
bind<Dio>().toProvide(() => dio( Annotate your DI modules and provider methods as shown above.
currentScope.resolve<String>(named: 'baseUrl')
)).singleton(); 3. **Run the generator**
``` ```
dart run build_runner build
# or with Flutter:
flutter pub run build_runner build
```
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
- **Customizing Parameter Names:**
Use the `@named('value')` annotation on methods and parameters for named bindings.
- **Runtime Arguments:**
Use `@params` to designate parameters as runtime arguments that are supplied at injection time.
- **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 ## 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>)