starting implement inject generator

This commit is contained in:
Sergey Penkovsky
2025-05-23 12:21:23 +03:00
parent 161e9085f4
commit 1f6ee172a1
13 changed files with 154 additions and 30 deletions

View File

@@ -19,3 +19,5 @@ export 'src/instance.dart';
export 'src/singleton.dart';
export 'src/named.dart';
export 'src/params.dart';
export 'src/inject.dart';
export 'src/injectable.dart';

View File

@@ -0,0 +1,17 @@
//
// 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 inject {
const inject();
}

View File

@@ -0,0 +1,17 @@
//
// 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 injectable {
const injectable();
}

View File

@@ -7,10 +7,21 @@ builders:
required_inputs: ["lib/**"]
runs_before: []
build_to: source
inject_generator:
import: "package:cherrypick_generator/inject_generator.dart"
builder_factories: ["injectBuilder"]
build_extensions: {".dart": [".cherrypick.g.dart"]}
auto_apply: dependents
required_inputs: ["lib/**"]
runs_before: []
build_to: source
targets:
$default:
builders:
cherrypick_generator|module_generator:
generate_for:
- lib/**.dart
cherrypick_generator|inject_generator:
generate_for:
- lib/**.dart

View File

@@ -14,3 +14,4 @@ library;
//
export 'module_generator.dart';
export 'inject_generator.dart';

View File

@@ -0,0 +1,65 @@
import 'dart:async';
import 'package:build/build.dart';
import 'package:collection/collection.dart';
import 'package:source_gen/source_gen.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:cherrypick_annotations/cherrypick_annotations.dart' as ann;
class InjectGenerator extends GeneratorForAnnotation<ann.injectable> {
const InjectGenerator();
@override
FutureOr<String> generateForAnnotatedElement(
Element element, ConstantReader annotation, BuildStep buildStep) {
// Only classes are supported for @module() annotation
// Обрабатываются только классы (другие элементы — ошибка)
if (element is! ClassElement) {
throw InvalidGenerationSourceError(
'@injectable() can only be applied to classes.',
element: element,
);
}
final classElement = element;
final className = classElement.name;
final mixinName = '_\$$className';
final injectedFields = classElement.fields.where((field) => field.metadata
.any((m) =>
m.computeConstantValue()?.type?.getDisplayString() == 'inject'));
// Генерируем инициализацию для каждого поля с аннотацией @inject()
final buffer = StringBuffer();
buffer.writeln('mixin $mixinName {');
buffer.writeln(' void _inject($className instance) {');
for (final field in injectedFields) {
// Получаем имя типа
final fieldType = field.type.getDisplayString();
// Ищем аннотацию @named
final namedAnnotation = field.metadata.firstWhereOrNull(
(m) => m.computeConstantValue()?.type?.getDisplayString() == 'named',
);
String namedParam = '';
if (namedAnnotation != null) {
final namedValue = namedAnnotation
.computeConstantValue()
?.getField('value')
?.toStringValue();
if (namedValue != null) {
namedParam = "(named: '$namedValue')";
}
}
buffer.writeln(
" instance.${field.name} = CherryPick.openRootScope().resolve<$fieldType>$namedParam;");
}
buffer.writeln(' }');
buffer.writeln('}');
return buffer.toString();
}
}
Builder injectBuilder(BuilderOptions options) =>
PartBuilder([InjectGenerator()], '.inject.cherrypick.g.dart');

View File

@@ -90,4 +90,4 @@ class ModuleGenerator extends GeneratorForAnnotation<ann.module> {
/// файлов, где встречается @module().
/// ---------------------------------------------------------------------------
Builder moduleBuilder(BuilderOptions options) =>
PartBuilder([ModuleGenerator()], '.cherrypick.g.dart');
PartBuilder([ModuleGenerator()], '.module.cherrypick.g.dart');

View File

@@ -18,6 +18,7 @@ dependencies:
build: ^2.4.1
build_runner: ^2.4.13
source_gen: ^1.5.0
collection: ^1.18.0
dev_dependencies:
lints: ^5.0.0

View File

@@ -153,7 +153,7 @@ packages:
path: "../../cherrypick_generator"
relative: true
source: path
version: "1.1.0-dev.1"
version: "1.1.0-dev.2"
clock:
dependency: transitive
description:

View File

@@ -0,0 +1,35 @@
import 'package:cherrypick/cherrypick.dart';
import 'package:cherrypick_annotations/cherrypick_annotations.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'domain/repository/post_repository.dart';
import 'presentation/bloc/post_bloc.dart';
import 'router/app_router.dart';
part 'app.inject.cherrypick.g.dart';
@injectable()
class MyApp extends StatelessWidget with _$MyApp {
final Scope scope;
final _appRouter = AppRouter();
@named('repo')
@inject()
late final PostRepository repository;
MyApp({super.key, required this.scope});
@override
Widget build(BuildContext context) {
_inject(this);
return BlocProvider(
create: (_) => PostBloc(repository),
child: MaterialApp.router(
routeInformationParser: _appRouter.defaultRouteParser(),
routerDelegate: _appRouter.delegate(),
theme: ThemeData.light(),
),
);
}
}

View File

@@ -5,7 +5,7 @@ import '../data/network/json_placeholder_api.dart';
import '../data/post_repository_impl.dart';
import '../domain/repository/post_repository.dart';
part 'app_module.cherrypick.g.dart';
part 'app_module.module.cherrypick.g.dart';
@module()
abstract class AppModule extends Module {

View File

@@ -1,10 +1,7 @@
import 'package:cherrypick/cherrypick.dart';
import 'package:flutter/material.dart';
import 'package:postly/app.dart';
import 'di/app_module.dart';
import 'domain/repository/post_repository.dart';
import 'presentation/bloc/post_bloc.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'router/app_router.dart';
void main() {
final scope = CherryPick.openRootScope();
@@ -12,25 +9,3 @@ void main() {
runApp(MyApp(scope: scope));
}
class MyApp extends StatelessWidget {
final Scope scope;
final _appRouter = AppRouter();
MyApp({super.key, required this.scope});
@override
Widget build(BuildContext context) {
// Получаем репозиторий через injector
final repository = scope.resolve<PostRepository>(named: 'repo');
return BlocProvider(
create: (_) => PostBloc(repository),
child: MaterialApp.router(
routeInformationParser: _appRouter.defaultRouteParser(),
routerDelegate: _appRouter.delegate(),
theme: ThemeData.light(),
),
);
}
}

View File

@@ -170,7 +170,7 @@ packages:
path: "../../cherrypick_generator"
relative: true
source: path
version: "1.1.0-dev.1"
version: "1.1.0-dev.2"
clock:
dependency: transitive
description: