Files
cherrypick/cherrypick_generator/lib/module_generator.dart

108 lines
4.1 KiB
Dart
Raw Normal View History

2025-05-18 15:43:02 +03:00
//
// Copyright 2021 Sergey Penkovsky (sergey.penkovsky@gmail.com)
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
2025-08-08 23:24:05 +03:00
// https://www.apache.org/licenses/LICENSE-2.0
2025-05-18 15:43:02 +03:00
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
2025-05-17 00:34:56 +03:00
import 'package:analyzer/dart/element/element.dart';
import 'package:build/build.dart';
import 'package:source_gen/source_gen.dart';
import 'package:cherrypick_annotations/cherrypick_annotations.dart' as ann;
2025-05-21 15:50:24 +03:00
import 'src/generated_class.dart';
2025-05-22 16:05:09 +03:00
/// ---------------------------------------------------------------------------
/// CherryPick Module Generator — Codegen for DI modules
2025-05-17 22:29:37 +03:00
///
/// This generator scans Dart classes annotated with `@module()` and generates
/// boilerplate for dependency injection registration automatically. Each public
/// method in such classes can be annotated to describe how an object should be
/// bound to the DI container (singleton, factory, named, with parameters, etc).
2025-05-20 19:50:13 +03:00
///
/// The generated code collects all such bind methods and produces a Dart
/// companion *module registration class* with a `.bindAll()` method, which you
/// can use from your DI root to automatically register those dependencies.
///
/// ## Example
/// ```dart
/// import 'package:cherrypick_annotations/cherrypick_annotations.dart';
///
/// @module()
/// abstract class AppModule {
/// @singleton()
/// Logger logger() => Logger();
///
/// @provide()
/// ApiService api(Logger logger) => ApiService(logger);
///
/// @named('dev')
/// FakeService fake() => FakeService();
/// }
/// ```
///
/// After codegen, you will get (simplified):
/// ```dart
/// class _\$AppModuleCherrypickModule extend AppModule {
/// static void bindAll(CherryPickScope scope, AppModule module) {
/// scope.addSingleton<Logger>(() => module.logger());
/// scope.addFactory<ApiService>(() => module.api(scope.resolve<Logger>()));
/// scope.addFactory<FakeService>(() => module.fake(), named: 'dev');
/// }
/// }
/// ```
///
/// Use it e.g. in your bootstrap:
/// ```dart
/// final scope = CherryPick.openRootScope()..intallModules([_\$AppModuleCherrypickModule()]);
/// ```
///
/// Features supported:
/// - Singleton, factory, named, parametric, and async providers
/// - Eliminates all boilerplate for DI registration
/// - Works with abstract classes and real classes
/// - Error if @module() is applied to a non-class
///
/// See also: [@singleton], [@provide], [@named], [@module]
2025-05-22 16:05:09 +03:00
/// ---------------------------------------------------------------------------
2025-05-17 00:34:56 +03:00
class ModuleGenerator extends GeneratorForAnnotation<ann.module> {
/// Generates Dart source for a class marked with the `@module()` annotation.
///
/// Throws [InvalidGenerationSourceError] if used on anything except a class.
2025-05-20 19:50:13 +03:00
///
/// See file-level docs for usage and generated output example.
2025-05-17 00:34:56 +03:00
@override
String generateForAnnotatedElement(
Element element,
ConstantReader annotation,
BuildStep buildStep,
) {
2025-05-17 00:34:56 +03:00
if (element is! ClassElement) {
throw InvalidGenerationSourceError(
'@module() can only be applied to classes.',
2025-05-17 00:34:56 +03:00
element: element,
);
}
2025-05-17 22:29:37 +03:00
2025-05-17 00:34:56 +03:00
final classElement = element;
2025-05-21 15:50:24 +03:00
final generatedClass = GeneratedClass.fromClassElement(classElement);
2025-05-20 19:50:13 +03:00
return generatedClass.generate();
}
}
2025-05-22 16:05:09 +03:00
/// ---------------------------------------------------------------------------
/// Codegen builder entry point: register this builder in build.yaml or your package.
2025-05-20 19:50:13 +03:00
///
/// Used by build_runner. Generates .module.cherrypick.g.dart files for each
/// source file with an annotated @module() class.
2025-05-22 16:05:09 +03:00
/// ---------------------------------------------------------------------------
2025-05-17 00:34:56 +03:00
Builder moduleBuilder(BuilderOptions options) =>
2025-05-23 12:21:23 +03:00
PartBuilder([ModuleGenerator()], '.module.cherrypick.g.dart');