write comments to code

This commit is contained in:
Sergey Penkovsky
2025-05-17 22:29:37 +03:00
parent 6e063a4067
commit 7bad0c09c0

View File

@@ -3,27 +3,63 @@ import 'package:build/build.dart';
import 'package:source_gen/source_gen.dart';
import 'package:cherrypick_annotations/cherrypick_annotations.dart' as ann;
/// Генератор DI-модулей для фреймворка cherrypick.
/// Генерирует расширение для класса с аннотацией @module,
/// автоматически создавая биндинги для зависимостей, определённых в модуле.
///
/// Пример:
/// ```dart
/// @module()
/// abstract class AppModule extends Module {
/// @singleton()
/// Dio dio() => Dio();
/// }
/// ```
///
/// Сгенерирует код:
/// ```dart
/// final class $AppModule extends AppModule {
/// @override
/// void builder(Scope currentScope) {
/// bind<Dio>().toProvide(() => dio()).singleton();
/// }
/// }
/// ```
class ModuleGenerator extends GeneratorForAnnotation<ann.module> {
/// Основной метод генерации кода для аннотированного класса-модуля.
/// [element] - класс с аннотацией @module.
/// [annotation], [buildStep] - служебные параметры build_runner.
/// Возвращает сгенерированный Dart-код класса-расширения.
@override
String generateForAnnotatedElement(
Element element,
ConstantReader annotation,
BuildStep buildStep,
) {
// Убеждаемся, что аннотирован только класс (не функция, не переменная).
if (element is! ClassElement) {
throw InvalidGenerationSourceError(
'@module() может быть применён только к классам.',
element: element,
);
}
final classElement = element;
final className = classElement.displayName;
// Имя сгенерированного класса (например: $AppModule)
final generatedClassName = r'$' + className;
final buffer = StringBuffer();
// Объявление генерируемого класса как final, который наследуется от исходного модуля
buffer.writeln('final class $generatedClassName extends $className {');
buffer.writeln(' @override');
buffer.writeln(' void builder(Scope currentScope) {');
// Обрабатываем все НЕ-абстрактные методы модуля
for (final method in classElement.methods.where((m) => !m.isAbstract)) {
// Проверка на наличие аннотации @singleton() у метода
final hasSingleton = method.metadata.any(
(m) =>
m
@@ -34,6 +70,8 @@ class ModuleGenerator extends GeneratorForAnnotation<ann.module> {
.contains('singleton') ??
false,
);
// Проверяем, есть ли у метода @named('...')
ElementAnnotation? namedMeta;
try {
namedMeta = method.metadata.firstWhere(
@@ -49,6 +87,8 @@ class ModuleGenerator extends GeneratorForAnnotation<ann.module> {
} catch (_) {
namedMeta = null;
}
// Извлекаем значение name из @named('value')
String? nameArg;
if (namedMeta != null) {
final cv = namedMeta.computeConstantValue();
@@ -57,8 +97,10 @@ class ModuleGenerator extends GeneratorForAnnotation<ann.module> {
}
}
// ЗДЕСЬ ЛОГИКА ДЛЯ ПАРАМЕТРОВ МЕТОДА
// Формируем список аргументов для вызова метода.
// Каждый параметр может быть с аннотацией @named, либо без.
final args = method.parameters.map((p) {
// Проверяем наличие @named('value') на параметре
ElementAnnotation? paramNamed;
try {
paramNamed = p.metadata.firstWhere(
@@ -79,14 +121,17 @@ class ModuleGenerator extends GeneratorForAnnotation<ann.module> {
if (paramNamed != null) {
final cv = paramNamed.computeConstantValue();
final namedValue = cv?.getField('value')?.toStringValue();
// Если указано имя для параметра (@named), пробрасываем его в resolve
if (namedValue != null) {
argExpr =
"currentScope.resolve<${p.type.getDisplayString(withNullability: false)}>(named: '$namedValue')";
} else {
// fallback — скобки все равно нужны
argExpr =
"currentScope.resolve<${p.type.getDisplayString(withNullability: false)}>()";
}
} else {
// Если параметр не @named - просто resolve по типу
argExpr =
"currentScope.resolve<${p.type.getDisplayString(withNullability: false)}>()";
}
@@ -96,7 +141,8 @@ class ModuleGenerator extends GeneratorForAnnotation<ann.module> {
final returnType =
method.returnType.getDisplayString(withNullability: false);
final methodName = method.displayName;
// С переносом строки, если есть параметры
// Если список параметров длинный — переносим вызов на новую строку для читаемости.
final hasLongArgs = args.length > 60 || args.contains('\n');
if (hasLongArgs) {
buffer.write(' bind<$returnType>()\n'
@@ -105,14 +151,17 @@ class ModuleGenerator extends GeneratorForAnnotation<ann.module> {
buffer.write(' bind<$returnType>()'
'.toProvide(() => $methodName($args))');
}
// Применяем имя биндера если есть @named
if (nameArg != null) {
buffer.write(".withName('$nameArg')");
}
// Применяем singleton если был @singleton
if (hasSingleton) {
buffer.write('.singleton()');
}
buffer.write(';\n');
}
buffer.writeln(' }');
buffer.writeln('}');
@@ -120,5 +169,7 @@ class ModuleGenerator extends GeneratorForAnnotation<ann.module> {
}
}
/// Фабрика Builder-го класса для build_runner
/// Производит .cherrypick.g.dart файл для каждого модуля.
Builder moduleBuilder(BuilderOptions options) =>
PartBuilder([ModuleGenerator()], '.cherrypick.g.dart');