diff --git a/cherrypick_annotations/lib/cherrypick_annotations.dart b/cherrypick_annotations/lib/cherrypick_annotations.dart index 8f0e469..4868c7f 100644 --- a/cherrypick_annotations/lib/cherrypick_annotations.dart +++ b/cherrypick_annotations/lib/cherrypick_annotations.dart @@ -3,6 +3,9 @@ /// More dartdocs go here. library; -export 'injectable.dart'; +export 'src/module.dart'; +export 'src/bind.dart'; +export 'src/provide.dart'; +export 'src/singleton.dart'; // TODO: Export any libraries intended for clients of this package. diff --git a/cherrypick_annotations/lib/src/bind.dart b/cherrypick_annotations/lib/src/bind.dart new file mode 100644 index 0000000..71dedfd --- /dev/null +++ b/cherrypick_annotations/lib/src/bind.dart @@ -0,0 +1,4 @@ +// ignore: camel_case_types +class Bind { + const Bind(); +} diff --git a/cherrypick_annotations/lib/injectable.dart b/cherrypick_annotations/lib/src/module.dart similarity index 64% rename from cherrypick_annotations/lib/injectable.dart rename to cherrypick_annotations/lib/src/module.dart index 0cc94ef..90e3f7f 100644 --- a/cherrypick_annotations/lib/injectable.dart +++ b/cherrypick_annotations/lib/src/module.dart @@ -1,6 +1,5 @@ -library; - /// Отмечает класс как injectable для автоматической регистрации. -class Injectable { - const Injectable(); +// ignore: camel_case_types +class module { + const module(); } diff --git a/cherrypick_annotations/lib/src/provide.dart b/cherrypick_annotations/lib/src/provide.dart new file mode 100644 index 0000000..61a687d --- /dev/null +++ b/cherrypick_annotations/lib/src/provide.dart @@ -0,0 +1,4 @@ +// ignore: camel_case_types +class provide { + const provide(); +} diff --git a/cherrypick_annotations/lib/src/singleton.dart b/cherrypick_annotations/lib/src/singleton.dart new file mode 100644 index 0000000..35eee76 --- /dev/null +++ b/cherrypick_annotations/lib/src/singleton.dart @@ -0,0 +1,4 @@ +// ignore: camel_case_types +class singleton { + const singleton(); +} diff --git a/cherrypick_generator/build.yaml b/cherrypick_generator/build.yaml index 9b10d4b..8181c17 100644 --- a/cherrypick_generator/build.yaml +++ b/cherrypick_generator/build.yaml @@ -1,8 +1,8 @@ builders: - injectable: - import: "package:cherrypick_generator/injectable_generator.dart" - builder_factories: ["injectableBuilder"] - build_extensions: {".dart": [".cherrypick_injectable.g.dart"]} + module_generator: + import: "package:cherrypick_generator/module_generator.dart" + builder_factories: ["moduleBuilder"] + build_extensions: {".dart": [".cherrypick.g.dart"]} auto_apply: dependents required_inputs: ["lib/**"] runs_before: [] @@ -11,6 +11,6 @@ builders: targets: $default: builders: - cherrypick_generator|injectable: + cherrypick_generator|module_generator: generate_for: - lib/**.dart \ No newline at end of file diff --git a/cherrypick_generator/lib/cherrypick_generator.dart b/cherrypick_generator/lib/cherrypick_generator.dart index 5f7c9d2..6528ebb 100644 --- a/cherrypick_generator/lib/cherrypick_generator.dart +++ b/cherrypick_generator/lib/cherrypick_generator.dart @@ -1,3 +1,3 @@ library; -export 'inject_generator.dart'; +export 'module_generator.dart'; diff --git a/cherrypick_generator/lib/inject_generator.dart b/cherrypick_generator/lib/inject_generator.dart deleted file mode 100644 index f8675bb..0000000 --- a/cherrypick_generator/lib/inject_generator.dart +++ /dev/null @@ -1,38 +0,0 @@ -import 'dart:async'; -import 'package:build/build.dart'; -import 'package:source_gen/source_gen.dart'; -import 'package:analyzer/dart/element/element.dart'; -import 'package:cherrypick_annotations/cherrypick_annotations.dart'; - -class InjectGenerator extends GeneratorForAnnotation { - @override - FutureOr generateForAnnotatedElement( - Element element, - ConstantReader annotation, - BuildStep buildStep, - ) { - print('[TRACE] Processing element: ${element.name}'); - - if (element is! FieldElement) { - throw InvalidGenerationSourceError( - 'Inject can only be used on fields.', - element: element, - ); - } - - print('[TRACE] Starting code generation for element: ${element.name}'); - - final className = element.enclosingElement.name; - final fieldName = element.name; - final fieldType = element.type.getDisplayString(withNullability: false); - final annotationName = annotation.read('named').stringValue; - - return ''' -extension \$${className}Inject on $className { - void init$fieldName() { - print("Injected $fieldType named '$annotationName' into $fieldName"); - } -} -'''; - } -} diff --git a/cherrypick_generator/lib/injectable_generator.dart b/cherrypick_generator/lib/injectable_generator.dart deleted file mode 100644 index 5015175..0000000 --- a/cherrypick_generator/lib/injectable_generator.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:source_gen/source_gen.dart'; -import 'package:analyzer/dart/element/element.dart'; -import 'package:build/build.dart'; -import 'package:cherrypick_annotations/cherrypick_annotations.dart'; - -class InjectableGenerator extends GeneratorForAnnotation { - @override - generateForAnnotatedElement( - Element element, ConstantReader annotation, BuildStep buildStep) { - if (element is! ClassElement) return null; - - final className = element.name; - - // Используйте уникальное имя функции (например, привязанное к файлу/классу) - return ''' -void \$initCherrypickGenerated() { - print("Generate code success $className"); -} -'''; - } -} - -Builder injectableBuilder(BuilderOptions options) => - PartBuilder([InjectableGenerator()], '.cherrypick_injectable.g.dart'); - -/* -Builder injectableBuilder(BuilderOptions options) => SharedPartBuilder( - [InjectableGenerator()], - 'injectable', - allowSyntaxErrors: true, - writeDescriptions: true, - ); -*/ diff --git a/cherrypick_generator/lib/module_generator.dart b/cherrypick_generator/lib/module_generator.dart new file mode 100644 index 0000000..fa4a436 --- /dev/null +++ b/cherrypick_generator/lib/module_generator.dart @@ -0,0 +1,60 @@ +// ... остальные импорты ... +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; + +class ModuleGenerator extends GeneratorForAnnotation { + @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; + final generatedClassName = r'$' + className; + final buffer = StringBuffer(); + + //buffer.writeln("part of '${buildStep.inputId.uri.pathSegments.last}';\n"); + 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)) { + final hasSingleton = method.metadata.any( + (m) => + m + .computeConstantValue() + ?.type + ?.getDisplayString(withNullability: false) + .toLowerCase() + .contains('singleton') ?? + false, + ); + if (!hasSingleton) continue; + + final returnType = + method.returnType.getDisplayString(withNullability: false); + final methodName = method.displayName; + final args = method.parameters + .map((p) => + "currentScope.resolve<${p.type.getDisplayString(withNullability: false)}>()") + .join(', '); + + buffer.write(' bind<$returnType>()' + '.toProvide(() => $methodName($args))' + '.singleton();\n'); + } + + buffer.writeln(' }\n}'); + return buffer.toString(); + } +} + +Builder moduleBuilder(BuilderOptions options) => + PartBuilder([ModuleGenerator()], '.cherrypick.g.dart'); diff --git a/examples/client_app/lib/foo.dart b/examples/client_app/lib/foo.dart deleted file mode 100644 index 6e0c2d6..0000000 --- a/examples/client_app/lib/foo.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:cherrypick_annotations/cherrypick_annotations.dart'; - -part 'foo.cherrypick_injectable.g.dart'; - -@Injectable() -class Foo { - late final String field; -} - -// где-то в main: -void iniFoo() { - $initCherrypickGenerated(); - // ... остальной код -} diff --git a/examples/client_app/lib/my_home_page.dart b/examples/client_app/lib/my_home_page.dart index 4b754ac..24697d6 100644 --- a/examples/client_app/lib/my_home_page.dart +++ b/examples/client_app/lib/my_home_page.dart @@ -1,10 +1,6 @@ -import 'package:cherrypick_annotations/cherrypick_annotations.dart'; import 'package:flutter/material.dart'; import 'use_case.dart'; -part 'my_home_page.cherrypick_injectable.g.dart'; - -@Injectable() class MyHomePage extends StatelessWidget { late final UseCase useCase; diff --git a/examples/client_app/lib/my_service.dart b/examples/client_app/lib/my_service.dart deleted file mode 100644 index 1225d15..0000000 --- a/examples/client_app/lib/my_service.dart +++ /dev/null @@ -1,12 +0,0 @@ -import 'package:cherrypick_annotations/cherrypick_annotations.dart'; - -part 'my_service.cherrypick_injectable.g.dart'; - -@Injectable() -class MyService {} - -// где-то в main: -void init() { - $initCherrypickGenerated(); - // ... остальной код -} diff --git a/examples/postly/lib/di/app_module.dart b/examples/postly/lib/di/app_module.dart index 5dd9637..6d0dc27 100644 --- a/examples/postly/lib/di/app_module.dart +++ b/examples/postly/lib/di/app_module.dart @@ -1,21 +1,20 @@ +import 'package:cherrypick_annotations/cherrypick_annotations.dart'; import 'package:dio/dio.dart'; import 'package:cherrypick/cherrypick.dart'; import '../data/network/json_placeholder_api.dart'; import '../data/post_repository_impl.dart'; import '../domain/repository/post_repository.dart'; -class AppModule extends Module { - @override - void builder(Scope currentScope) { - bind().toProvide(() => Dio()).singleton(); +part 'app_module.cherrypick.g.dart'; - bind() - .toProvide(() => JsonPlaceholderApi(currentScope.resolve())) - .singleton(); +@module() +abstract class AppModule extends Module { + @singleton() + Dio dio() => Dio(); - bind() - .toProvide(() => - PostRepositoryImpl(currentScope.resolve())) - .singleton(); - } + @singleton() + JsonPlaceholderApi api(Dio dio) => JsonPlaceholderApi(dio); + + @singleton() + PostRepository repo(JsonPlaceholderApi api) => PostRepositoryImpl(api); } diff --git a/examples/postly/lib/main.dart b/examples/postly/lib/main.dart index 394fbc2..1307d5f 100644 --- a/examples/postly/lib/main.dart +++ b/examples/postly/lib/main.dart @@ -8,7 +8,7 @@ import 'router/app_router.dart'; void main() { final scope = CherryPick.openRootScope(); - scope.installModules([AppModule()]); + scope.installModules([$AppModule()]); runApp(MyApp(scope: scope)); } diff --git a/examples/postly/pubspec.lock b/examples/postly/pubspec.lock index 880c933..ede55b7 100644 --- a/examples/postly/pubspec.lock +++ b/examples/postly/pubspec.lock @@ -156,7 +156,21 @@ packages: path: "../../cherrypick" relative: true source: path - version: "2.1.0" + version: "2.1.0-dev.1" + cherrypick_annotations: + dependency: "direct main" + description: + path: "../../cherrypick_annotations" + relative: true + source: path + version: "1.0.0" + cherrypick_generator: + dependency: "direct dev" + description: + path: "../../cherrypick_generator" + relative: true + source: path + version: "1.0.0" clock: dependency: transitive description: diff --git a/examples/postly/pubspec.yaml b/examples/postly/pubspec.yaml index b8e74b7..ca6b243 100644 --- a/examples/postly/pubspec.yaml +++ b/examples/postly/pubspec.yaml @@ -12,7 +12,8 @@ dependencies: flutter: sdk: flutter - cherrypick: ^2.1.0 + cherrypick: any + cherrypick_annotations: any dio: ^5.4.0 retrofit: ^4.0.3 @@ -29,7 +30,9 @@ dev_dependencies: flutter_lints: ^4.0.0 - build_runner: ^2.4.6 + cherrypick_generator: any + build_runner: any + retrofit_generator: ^8.0.4 freezed: ^2.3.2 json_serializable: any diff --git a/examples/postly/test/widget_test.dart b/examples/postly/test/widget_test.dart index 6ac7ee0..42b46fb 100644 --- a/examples/postly/test/widget_test.dart +++ b/examples/postly/test/widget_test.dart @@ -17,7 +17,7 @@ void main() { setUp(() { scope = CherryPick.openRootScope(); - scope.installModules([AppModule()]); + scope.installModules([$AppModule()]); }); testWidgets('Counter increments smoke test', (WidgetTester tester) async { expect(1, 1);