# Cherrypick Generator **Cherrypick Generator** is a Dart code generation library for automating dependency injection (DI) boilerplate. It processes classes and fields annotated with [cherrypick_annotations](https://pub.dev/packages/cherrypick_annotations) and generates registration code for services, modules, and field injection for classes marked as `@injectable`. It supports advanced DI features such as scopes, named bindings, parameters, and asynchronous dependencies. --- ### Advanced: Customizing Generated File Paths (`build_extensions`) You can further control the filenames and subdirectory structure of generated files using the `build_extensions` option in `build.yaml`. This is especially useful in large apps for keeping DI artifacts organized under `lib/generated/` or any custom location. **Example advanced build.yaml:** ```yaml targets: $default: builders: cherrypick_generator|inject_generator: options: build_extensions: '^lib/app.dart': ['lib/generated/app.inject.cherrypick.g.dart'] output_dir: lib/generated generate_for: - lib/**.dart cherrypick_generator|module_generator: options: build_extensions: '^lib/di/{{}}.dart': ['lib/generated/di/{{}}.module.cherrypick.g.dart'] output_dir: lib/generated generate_for: - lib/**.dart ``` - **output_dir**: Path where all generated files are placed (e.g., `lib/generated`) - **build_extensions**: Allows templating of generated filenames and locations. You can use wildcards like `{{}}` to keep directory structure or group related files. **If you use these options, be sure to update your imports accordingly, for example:** ```dart import 'package:your_package/generated/app.inject.cherrypick.g.dart'; import 'package:your_package/generated/di/app_module.module.cherrypick.g.dart'; ``` ### FAQ / Troubleshooting - If files are missing or located in unexpected directories, double-check your `output_dir` and `build_extensions` configuration. - If you change generation paths, always update your imports in the codebase. - These options are backward compatible: omitting them preserves pre-existing (side-by-source) output behavior. --- ## Features - **Automatic Field Injection:** Detects classes annotated with `@injectable()`, and generates mixins to inject all fields annotated with `@inject()`, supporting scope and named qualifiers. - **Module and Service Registration:** For classes annotated with `@module()`, generates service registration code for methods using annotations such as `@provide`, `@instance`, `@singleton`, `@named`, and `@params`. - **Scope & Named Qualifier Support:** Supports advanced DI features:   • Field-level scoping with `@scope('scopename')`   • Named dependencies via `@named('value')` - **Synchronous & Asynchronous Support:** Handles both synchronous and asynchronous services (including `Future`) for both field injection and module registration. - **Parameters and Runtime Arguments:** Recognizes and wires both injected dependencies and runtime parameters using `@params`. - **Error Handling:** Validates annotations at generation time. Provides helpful errors for incorrect usage (e.g., using `@injectable` on non-class elements). --- ## How It Works ### 1. Annotate your code Use annotations from [cherrypick_annotations](https://pub.dev/packages/cherrypick_annotations): - `@injectable()` — on classes to enable field injection - `@inject()` — on fields to specify they should be injected - `@scope()`, `@named()` — on fields or parameters for advanced wiring - `@module()` — on classes to mark as DI modules - `@provide`, `@instance`, `@singleton`, `@params` — on methods and parameters for module-based DI ### 2. Run the generator Use `build_runner` to process your code and generate `.module.cherrypick.g.dart` and `.inject.cherrypick.g.dart` files. ### 3. Use the output in your application - For modules: Register DI providers using the generated `$YourModule` class. - For services: Enable field injection on classes using the generated mixin. --- ## Field Injection Example Given the following: ```dart import 'package:cherrypick_annotations/cherrypick_annotations.dart'; @injectable() class MyWidget with _$MyWidget { @inject() late final AuthService auth; @inject() @scope('profile') late final ProfileManager manager; @inject() @named('special') late final ApiClient specialApi; } ``` **The generator will output (simplified):** ```dart mixin _$MyWidget { void _inject(MyWidget instance) { instance.auth = CherryPick.openRootScope().resolve(); instance.manager = CherryPick.openScope(scopeName: 'profile').resolve(); instance.specialApi = CherryPick.openRootScope().resolve(named: 'special'); } } ``` You can then mix this into your widget to enable automatic DI at runtime. --- ## Module Registration Example Given: ```dart import 'package:cherrypick_annotations/cherrypick_annotations.dart'; @module() class MyModule { @singleton @instance AuthService provideAuth(Api api); @provide @named('logging') Future provideLogger(@params Map args); } ``` **The generator will output (simplified):** ```dart final class $MyModule extends MyModule { @override void builder(Scope currentScope) { bind() .toInstance(provideAuth(currentScope.resolve())) .singleton(); bind() .toProvideAsyncWithParams((args) => provideLogger(args)) .withName('logging'); } } ``` --- ## Key Points - **Rich Annotation Support:** Mix and match field, parameter, and method annotations for maximum flexibility. - **Scope and Named Resolution:** Use `@scope('...')` and `@named('...')` to precisely control where and how dependencies are wired. - **Async/Synchronous:** The generator distinguishes between sync (`resolve`) and async (`resolveAsync`) dependencies. - **Automatic Mixins:** For classes with `@injectable()`, a mixin is generated that injects all relevant fields (using constructor or setter). - **Comprehensive Error Checking:** Misapplied annotations (e.g., `@injectable()` on non-class) produce clear build-time errors. --- ## Usage 1. **Add dependencies** ```yaml dependencies: cherrypick_annotations: ^latest dev_dependencies: cherrypick_generator: ^latest build_runner: ^2.1.0 ``` 2. **Annotate your classes and modules as above** 3. **Run the generator** ```shell dart run build_runner build # or, if using Flutter: flutter pub run build_runner build ``` 4. **Use generated code** - Import the generated `.inject.cherrypick.g.dart` or `.cherrypick.g.dart` files where needed --- ## Advanced Usage ### Custom output directory for generated code (output_dir) You can control the directory where the generated files (`*.inject.cherrypick.g.dart`, `*.module.cherrypick.g.dart`) are placed using the `output_dir` option in your `build.yaml`: ```yaml targets: $default: builders: cherrypick_generator|injectBuilder: options: output_dir: lib/generated cherrypick_generator|moduleBuilder: options: output_dir: lib/generated ``` **If `output_dir` is omitted, generated files are placed next to the original sources (default behavior).** After running code generation, you will find files like `lib/generated/app.inject.cherrypick.g.dart` and `lib/generated/your_module.module.cherrypick.g.dart`. You can import them as needed from that directory. - **Combining Modules and Field Injection:** It's possible to mix both style of DI — modules for binding, and field injection for consuming services. - **Parameter and Named Injection:** Use `@named` on both provider and parameter for named registration and lookup; use `@params` to pass runtime arguments. - **Async Factories:** Methods returning Future generate async bindings and async field resolution logic. --- ## Developer Notes - The generator relies on the Dart analyzer, `source_gen`, and `build` packages. - All classes and methods are parsed for annotations. - Improper annotation usage will result in generator errors. --- ## License ``` Licensed under the Apache License, Version 2.0 ``` --- ## Contribution Pull requests and issues are welcome! Please open GitHub issues or submit improvements. ---