refactor inject generator

This commit is contained in:
Sergey Penkovsky
2025-05-23 16:03:29 +03:00
parent 8438697107
commit 7dbaa59c01

View File

@@ -11,7 +11,10 @@ class InjectGenerator extends GeneratorForAnnotation<ann.injectable> {
@override @override
FutureOr<String> generateForAnnotatedElement( FutureOr<String> generateForAnnotatedElement(
Element element, ConstantReader annotation, BuildStep buildStep) { Element element,
ConstantReader annotation,
BuildStep buildStep,
) {
if (element is! ClassElement) { if (element is! ClassElement) {
throw InvalidGenerationSourceError( throw InvalidGenerationSourceError(
'@injectable() can only be applied to classes.', '@injectable() can only be applied to classes.',
@@ -23,20 +26,39 @@ class InjectGenerator extends GeneratorForAnnotation<ann.injectable> {
final className = classElement.name; final className = classElement.name;
final mixinName = '_\$$className'; final mixinName = '_\$$className';
final injectedFields = classElement.fields.where((field) => field.metadata final buffer = StringBuffer()
.any((m) => ..writeln('mixin $mixinName {')
m.computeConstantValue()?.type?.getDisplayString() == 'inject')); ..writeln(' void _inject($className instance) {');
final buffer = StringBuffer(); // Collect and process all @inject fields
buffer.writeln('mixin $mixinName {'); final injectFields =
buffer.writeln(' void _inject($className instance) {'); classElement.fields.where(_isInjectField).map(_parseInjectField);
for (final field in injectedFields) { for (final parsedField in injectFields) {
buffer.writeln(_generateInjectionLine(parsedField));
}
buffer
..writeln(' }')
..writeln('}');
return buffer.toString();
}
// Checks if a field has @inject annotation
static bool _isInjectField(FieldElement field) {
return field.metadata.any(
(m) => m.computeConstantValue()?.type?.getDisplayString() == 'inject',
);
}
// Parsed structure storage
static _ParsedInjectField _parseInjectField(FieldElement field) {
String? scopeName; String? scopeName;
String? namedValue; String? namedValue;
for (final m in field.metadata) { for (final meta in field.metadata) {
final DartObject? obj = m.computeConstantValue(); final DartObject? obj = meta.computeConstantValue();
final type = obj?.type?.getDisplayString(); final type = obj?.type?.getDisplayString();
if (type == 'scope') { if (type == 'scope') {
scopeName = obj?.getField('name')?.toStringValue(); scopeName = obj?.getField('name')?.toStringValue();
@@ -45,39 +67,58 @@ class InjectGenerator extends GeneratorForAnnotation<ann.injectable> {
} }
} }
// --- Фикс: определяем resolveAsync для Future<T> --- final DartType dartType = field.type;
final DartType type = field.type; String coreTypeName;
String genericType; bool isFuture;
String resolveMethod;
if (type.isDartAsyncFuture) { if (dartType.isDartAsyncFuture) {
// Если поле Future<T> final ParameterizedType paramType = dartType as ParameterizedType;
final typeArg = (type as ParameterizedType).typeArguments.first; coreTypeName = paramType.typeArguments.first.getDisplayString();
genericType = typeArg.getDisplayString(); isFuture = true;
resolveMethod = 'resolveAsync<$genericType>';
} else { } else {
genericType = type.getDisplayString(); coreTypeName = dartType.getDisplayString();
resolveMethod = 'resolve<$genericType>'; isFuture = false;
} }
// Вызываем openScope или openRootScope return _ParsedInjectField(
String accessor = (scopeName != null && scopeName.isNotEmpty) fieldName: field.name,
? "CherryPick.openScope(scopeName: '$scopeName').$resolveMethod" coreType: coreTypeName,
: "CherryPick.openRootScope().$resolveMethod"; isFuture: isFuture,
scopeName: scopeName,
namedValue: namedValue,
);
}
// Аргументы resolve // Generates the injection invocation line for the field
String params = (namedValue != null && namedValue.isNotEmpty) String _generateInjectionLine(_ParsedInjectField field) {
? "(named: '$namedValue')" final methodName = field.isFuture
? 'resolveAsync<${field.coreType}>'
: 'resolve<${field.coreType}>';
final openCall = (field.scopeName != null && field.scopeName!.isNotEmpty)
? "CherryPick.openScope(scopeName: '${field.scopeName}')"
: "CherryPick.openRootScope()";
final params = (field.namedValue != null && field.namedValue!.isNotEmpty)
? "(named: '${field.namedValue}')"
: '()'; : '()';
buffer.writeln(" instance.${field.name} = $accessor$params;"); return " instance.${field.fieldName} = $openCall.$methodName$params;";
} }
}
buffer.writeln(' }'); class _ParsedInjectField {
buffer.writeln('}'); final String fieldName;
final String coreType;
final bool isFuture;
final String? scopeName;
final String? namedValue;
return buffer.toString(); _ParsedInjectField({
} required this.fieldName,
required this.coreType,
required this.isFuture,
this.scopeName,
this.namedValue,
});
} }
Builder injectBuilder(BuilderOptions options) => Builder injectBuilder(BuilderOptions options) =>