feat: implement InjectGenerator

This commit is contained in:
Sergey Penkovsky
2025-05-23 14:08:08 +03:00
parent 1f6ee172a1
commit 9c42ba4cef
5 changed files with 55 additions and 26 deletions

View File

@@ -21,3 +21,4 @@ export 'src/named.dart';
export 'src/params.dart'; export 'src/params.dart';
export 'src/inject.dart'; export 'src/inject.dart';
export 'src/injectable.dart'; export 'src/injectable.dart';
export 'src/scope.dart';

View File

@@ -0,0 +1,18 @@
//
// 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
// http://www.apache.org/licenses/LICENSE-2.0
// 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.
//
// ignore: camel_case_types
final class scope {
final String? name;
const scope(this.name);
}

View File

@@ -1,6 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:analyzer/dart/constant/value.dart';
import 'package:build/build.dart'; import 'package:build/build.dart';
import 'package:collection/collection.dart';
import 'package:source_gen/source_gen.dart'; import 'package:source_gen/source_gen.dart';
import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/dart/element/element.dart';
import 'package:cherrypick_annotations/cherrypick_annotations.dart' as ann; import 'package:cherrypick_annotations/cherrypick_annotations.dart' as ann;
@@ -11,8 +11,6 @@ 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) {
// Only classes are supported for @module() annotation
// Обрабатываются только классы (другие элементы — ошибка)
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.',
@@ -28,30 +26,36 @@ class InjectGenerator extends GeneratorForAnnotation<ann.injectable> {
.any((m) => .any((m) =>
m.computeConstantValue()?.type?.getDisplayString() == 'inject')); m.computeConstantValue()?.type?.getDisplayString() == 'inject'));
// Генерируем инициализацию для каждого поля с аннотацией @inject()
final buffer = StringBuffer(); final buffer = StringBuffer();
buffer.writeln('mixin $mixinName {'); buffer.writeln('mixin $mixinName {');
buffer.writeln(' void _inject($className instance) {'); buffer.writeln(' void _inject($className instance) {');
for (final field in injectedFields) { for (final field in injectedFields) {
// Получаем имя типа String? scopeName;
final fieldType = field.type.getDisplayString(); String? namedValue;
// Ищем аннотацию @named
final namedAnnotation = field.metadata.firstWhereOrNull( for (final m in field.metadata) {
(m) => m.computeConstantValue()?.type?.getDisplayString() == 'named', final DartObject? obj = m.computeConstantValue();
); final type = obj?.type?.getDisplayString();
String namedParam = ''; if (type == 'scope') {
if (namedAnnotation != null) { scopeName = obj?.getField('name')?.toStringValue();
final namedValue = namedAnnotation } else if (type == 'named') {
.computeConstantValue() namedValue = obj?.getField('value')?.toStringValue();
?.getField('value')
?.toStringValue();
if (namedValue != null) {
namedParam = "(named: '$namedValue')";
} }
} }
buffer.writeln(
" instance.${field.name} = CherryPick.openRootScope().resolve<$fieldType>$namedParam;"); final String fieldType = field.type.getDisplayString();
String accessor = (scopeName != null && scopeName.isNotEmpty)
? "CherryPick.openScope(scopeName: '$scopeName').resolve"
: "CherryPick.openRootScope().resolve";
String generic = fieldType != 'dynamic' ? '<$fieldType>' : '';
String params = (namedValue != null && namedValue.isNotEmpty)
? "(named: '$namedValue')"
: '()';
buffer.writeln(" instance.${field.name} = $accessor$generic$params;");
} }
buffer.writeln(' }'); buffer.writeln(' }');

View File

@@ -11,14 +11,22 @@ part 'app.inject.cherrypick.g.dart';
@injectable() @injectable()
class MyApp extends StatelessWidget with _$MyApp { class MyApp extends StatelessWidget with _$MyApp {
final Scope scope;
final _appRouter = AppRouter(); final _appRouter = AppRouter();
@scope('authZone')
@inject()
late final String text;
@scope('authZone')
@named('timeout')
@inject()
late final int timeout;
@named('repo') @named('repo')
@inject() @inject()
late final PostRepository repository; late final PostRepository repository;
MyApp({super.key, required this.scope}); MyApp({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@@ -4,8 +4,6 @@ import 'package:postly/app.dart';
import 'di/app_module.dart'; import 'di/app_module.dart';
void main() { void main() {
final scope = CherryPick.openRootScope(); CherryPick.openRootScope().installModules([$AppModule()]);
scope.installModules([$AppModule()]); runApp(MyApp());
runApp(MyApp(scope: scope));
} }