mirror of
https://github.com/pese-git/cherrypick.git
synced 2026-01-23 21:13:35 +00:00
feat: implement generator di module
This commit is contained in:
@@ -3,6 +3,9 @@
|
|||||||
/// More dartdocs go here.
|
/// More dartdocs go here.
|
||||||
library;
|
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.
|
// TODO: Export any libraries intended for clients of this package.
|
||||||
|
|||||||
4
cherrypick_annotations/lib/src/bind.dart
Normal file
4
cherrypick_annotations/lib/src/bind.dart
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
// ignore: camel_case_types
|
||||||
|
class Bind {
|
||||||
|
const Bind();
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
library;
|
|
||||||
|
|
||||||
/// Отмечает класс как injectable для автоматической регистрации.
|
/// Отмечает класс как injectable для автоматической регистрации.
|
||||||
class Injectable {
|
// ignore: camel_case_types
|
||||||
const Injectable();
|
class module {
|
||||||
|
const module();
|
||||||
}
|
}
|
||||||
4
cherrypick_annotations/lib/src/provide.dart
Normal file
4
cherrypick_annotations/lib/src/provide.dart
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
// ignore: camel_case_types
|
||||||
|
class provide {
|
||||||
|
const provide();
|
||||||
|
}
|
||||||
4
cherrypick_annotations/lib/src/singleton.dart
Normal file
4
cherrypick_annotations/lib/src/singleton.dart
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
// ignore: camel_case_types
|
||||||
|
class singleton {
|
||||||
|
const singleton();
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
builders:
|
builders:
|
||||||
injectable:
|
module_generator:
|
||||||
import: "package:cherrypick_generator/injectable_generator.dart"
|
import: "package:cherrypick_generator/module_generator.dart"
|
||||||
builder_factories: ["injectableBuilder"]
|
builder_factories: ["moduleBuilder"]
|
||||||
build_extensions: {".dart": [".cherrypick_injectable.g.dart"]}
|
build_extensions: {".dart": [".cherrypick.g.dart"]}
|
||||||
auto_apply: dependents
|
auto_apply: dependents
|
||||||
required_inputs: ["lib/**"]
|
required_inputs: ["lib/**"]
|
||||||
runs_before: []
|
runs_before: []
|
||||||
@@ -11,6 +11,6 @@ builders:
|
|||||||
targets:
|
targets:
|
||||||
$default:
|
$default:
|
||||||
builders:
|
builders:
|
||||||
cherrypick_generator|injectable:
|
cherrypick_generator|module_generator:
|
||||||
generate_for:
|
generate_for:
|
||||||
- lib/**.dart
|
- lib/**.dart
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
library;
|
library;
|
||||||
|
|
||||||
export 'inject_generator.dart';
|
export 'module_generator.dart';
|
||||||
|
|||||||
@@ -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<Injectable> {
|
|
||||||
@override
|
|
||||||
FutureOr<String> 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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
''';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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<Injectable> {
|
|
||||||
@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,
|
|
||||||
);
|
|
||||||
*/
|
|
||||||
60
cherrypick_generator/lib/module_generator.dart
Normal file
60
cherrypick_generator/lib/module_generator.dart
Normal file
@@ -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<ann.module> {
|
||||||
|
@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');
|
||||||
@@ -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();
|
|
||||||
// ... остальной код
|
|
||||||
}
|
|
||||||
@@ -1,10 +1,6 @@
|
|||||||
import 'package:cherrypick_annotations/cherrypick_annotations.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'use_case.dart';
|
import 'use_case.dart';
|
||||||
|
|
||||||
part 'my_home_page.cherrypick_injectable.g.dart';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
class MyHomePage extends StatelessWidget {
|
class MyHomePage extends StatelessWidget {
|
||||||
late final UseCase useCase;
|
late final UseCase useCase;
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
|
||||||
// ... остальной код
|
|
||||||
}
|
|
||||||
@@ -1,21 +1,20 @@
|
|||||||
|
import 'package:cherrypick_annotations/cherrypick_annotations.dart';
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:cherrypick/cherrypick.dart';
|
import 'package:cherrypick/cherrypick.dart';
|
||||||
import '../data/network/json_placeholder_api.dart';
|
import '../data/network/json_placeholder_api.dart';
|
||||||
import '../data/post_repository_impl.dart';
|
import '../data/post_repository_impl.dart';
|
||||||
import '../domain/repository/post_repository.dart';
|
import '../domain/repository/post_repository.dart';
|
||||||
|
|
||||||
class AppModule extends Module {
|
part 'app_module.cherrypick.g.dart';
|
||||||
@override
|
|
||||||
void builder(Scope currentScope) {
|
|
||||||
bind<Dio>().toProvide(() => Dio()).singleton();
|
|
||||||
|
|
||||||
bind<JsonPlaceholderApi>()
|
@module()
|
||||||
.toProvide(() => JsonPlaceholderApi(currentScope.resolve<Dio>()))
|
abstract class AppModule extends Module {
|
||||||
.singleton();
|
@singleton()
|
||||||
|
Dio dio() => Dio();
|
||||||
|
|
||||||
bind<PostRepository>()
|
@singleton()
|
||||||
.toProvide(() =>
|
JsonPlaceholderApi api(Dio dio) => JsonPlaceholderApi(dio);
|
||||||
PostRepositoryImpl(currentScope.resolve<JsonPlaceholderApi>()))
|
|
||||||
.singleton();
|
@singleton()
|
||||||
}
|
PostRepository repo(JsonPlaceholderApi api) => PostRepositoryImpl(api);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import 'router/app_router.dart';
|
|||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
final scope = CherryPick.openRootScope();
|
final scope = CherryPick.openRootScope();
|
||||||
scope.installModules([AppModule()]);
|
scope.installModules([$AppModule()]);
|
||||||
|
|
||||||
runApp(MyApp(scope: scope));
|
runApp(MyApp(scope: scope));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -156,7 +156,21 @@ packages:
|
|||||||
path: "../../cherrypick"
|
path: "../../cherrypick"
|
||||||
relative: true
|
relative: true
|
||||||
source: path
|
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:
|
clock:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ dependencies:
|
|||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|
||||||
cherrypick: ^2.1.0
|
cherrypick: any
|
||||||
|
cherrypick_annotations: any
|
||||||
|
|
||||||
dio: ^5.4.0
|
dio: ^5.4.0
|
||||||
retrofit: ^4.0.3
|
retrofit: ^4.0.3
|
||||||
@@ -29,7 +30,9 @@ dev_dependencies:
|
|||||||
|
|
||||||
flutter_lints: ^4.0.0
|
flutter_lints: ^4.0.0
|
||||||
|
|
||||||
build_runner: ^2.4.6
|
cherrypick_generator: any
|
||||||
|
build_runner: any
|
||||||
|
|
||||||
retrofit_generator: ^8.0.4
|
retrofit_generator: ^8.0.4
|
||||||
freezed: ^2.3.2
|
freezed: ^2.3.2
|
||||||
json_serializable: any
|
json_serializable: any
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ void main() {
|
|||||||
|
|
||||||
setUp(() {
|
setUp(() {
|
||||||
scope = CherryPick.openRootScope();
|
scope = CherryPick.openRootScope();
|
||||||
scope.installModules([AppModule()]);
|
scope.installModules([$AppModule()]);
|
||||||
});
|
});
|
||||||
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
||||||
expect(1, 1);
|
expect(1, 1);
|
||||||
|
|||||||
Reference in New Issue
Block a user