mirror of
https://github.com/pese-git/cherrypick.git
synced 2026-01-24 13:47:24 +00:00
Compare commits
6 Commits
cherrypick
...
cherrypick
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
21955640d9 | ||
|
|
a62052daa5 | ||
|
|
7dbaa59c01 | ||
|
|
8438697107 | ||
|
|
9c42ba4cef | ||
|
|
1f6ee172a1 |
26
CHANGELOG.md
26
CHANGELOG.md
@@ -3,6 +3,32 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## 2025-05-23
|
||||
|
||||
### Changes
|
||||
|
||||
---
|
||||
|
||||
Packages with breaking changes:
|
||||
|
||||
- There are no breaking changes in this release.
|
||||
|
||||
Packages with other changes:
|
||||
|
||||
- [`cherrypick_annotations` - `v1.1.0-dev.1`](#cherrypick_annotations---v110-dev1)
|
||||
- [`cherrypick_generator` - `v1.1.0-dev.3`](#cherrypick_generator---v110-dev3)
|
||||
|
||||
---
|
||||
|
||||
#### `cherrypick_annotations` - `v1.1.0-dev.1`
|
||||
|
||||
- **FEAT**: implement InjectGenerator.
|
||||
|
||||
#### `cherrypick_generator` - `v1.1.0-dev.3`
|
||||
|
||||
- **FEAT**: implement InjectGenerator.
|
||||
|
||||
|
||||
## 2025-05-23
|
||||
|
||||
### Changes
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 1.1.0-dev.1
|
||||
|
||||
- **FEAT**: implement InjectGenerator.
|
||||
|
||||
## 1.1.0-dev.0
|
||||
|
||||
- **FEAT**: implement generator for dynamic params.
|
||||
|
||||
@@ -19,3 +19,6 @@ export 'src/instance.dart';
|
||||
export 'src/singleton.dart';
|
||||
export 'src/named.dart';
|
||||
export 'src/params.dart';
|
||||
export 'src/inject.dart';
|
||||
export 'src/injectable.dart';
|
||||
export 'src/scope.dart';
|
||||
|
||||
20
cherrypick_annotations/lib/src/inject.dart
Normal file
20
cherrypick_annotations/lib/src/inject.dart
Normal file
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
@experimental
|
||||
// ignore: camel_case_types
|
||||
final class inject {
|
||||
const inject();
|
||||
}
|
||||
20
cherrypick_annotations/lib/src/injectable.dart
Normal file
20
cherrypick_annotations/lib/src/injectable.dart
Normal file
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
@experimental
|
||||
// ignore: camel_case_types
|
||||
final class injectable {
|
||||
const injectable();
|
||||
}
|
||||
21
cherrypick_annotations/lib/src/scope.dart
Normal file
21
cherrypick_annotations/lib/src/scope.dart
Normal file
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
@experimental
|
||||
// ignore: camel_case_types
|
||||
final class scope {
|
||||
final String? name;
|
||||
const scope(this.name);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
name: cherrypick_annotations
|
||||
description: |
|
||||
Set of annotations for CherryPick dependency injection library. Enables code generation and declarative DI for Dart & Flutter projects.
|
||||
version: 1.1.0-dev.0
|
||||
version: 1.1.0-dev.1
|
||||
documentation: https://github.com/pese-git/cherrypick/wiki
|
||||
repository: https://github.com/pese-git/cherrypick/cherrypick_annotations
|
||||
issue_tracker: https://github.com/pese-git/cherrypick/issues
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 1.1.0-dev.3
|
||||
|
||||
- **FEAT**: implement InjectGenerator.
|
||||
|
||||
## 1.1.0-dev.2
|
||||
|
||||
- **FIX**: update instance generator code.
|
||||
|
||||
@@ -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
|
||||
@@ -14,3 +14,4 @@ library;
|
||||
//
|
||||
|
||||
export 'module_generator.dart';
|
||||
export 'inject_generator.dart';
|
||||
|
||||
186
cherrypick_generator/lib/inject_generator.dart
Normal file
186
cherrypick_generator/lib/inject_generator.dart
Normal file
@@ -0,0 +1,186 @@
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
import 'dart:async';
|
||||
import 'package:analyzer/dart/constant/value.dart';
|
||||
import 'package:analyzer/dart/element/type.dart';
|
||||
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' as ann;
|
||||
|
||||
/// InjectGenerator generates a mixin for a class marked with @injectable()
|
||||
/// and injects all fields annotated with @inject(), using CherryPick DI.
|
||||
///
|
||||
/// For Future<T> fields it calls .resolveAsync<T>(),
|
||||
/// otherwise .resolve<T>() is used. Scope and named qualifiers are supported.
|
||||
///
|
||||
/// ---
|
||||
///
|
||||
/// InjectGenerator генерирует миксин для класса с аннотацией @injectable()
|
||||
/// и внедряет все поля, помеченные @inject(), используя DI-фреймворк CherryPick.
|
||||
///
|
||||
/// Для Future<T> полей вызывается .resolveAsync<T>(),
|
||||
/// для остальных — .resolve<T>(). Поддерживаются scope и named qualifier.
|
||||
///
|
||||
class InjectGenerator extends GeneratorForAnnotation<ann.injectable> {
|
||||
const InjectGenerator();
|
||||
|
||||
/// The main entry point for code generation.
|
||||
///
|
||||
/// Checks class validity, collects injectable fields, and produces injection code.
|
||||
///
|
||||
/// Основная точка входа генератора. Проверяет класс, собирает инъектируемые поля и создает код внедрения зависимостей.
|
||||
@override
|
||||
FutureOr<String> generateForAnnotatedElement(
|
||||
Element element,
|
||||
ConstantReader annotation,
|
||||
BuildStep buildStep,
|
||||
) {
|
||||
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 buffer = StringBuffer()
|
||||
..writeln('mixin $mixinName {')
|
||||
..writeln(' void _inject($className instance) {');
|
||||
|
||||
// Collect and process all @inject fields.
|
||||
// Собираем и обрабатываем все поля с @inject.
|
||||
final injectFields =
|
||||
classElement.fields.where(_isInjectField).map(_parseInjectField);
|
||||
|
||||
for (final parsedField in injectFields) {
|
||||
buffer.writeln(_generateInjectionLine(parsedField));
|
||||
}
|
||||
|
||||
buffer
|
||||
..writeln(' }')
|
||||
..writeln('}');
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/// Checks if a field has the @inject annotation.
|
||||
///
|
||||
/// Проверяет, отмечено ли поле аннотацией @inject.
|
||||
static bool _isInjectField(FieldElement field) {
|
||||
return field.metadata.any(
|
||||
(m) => m.computeConstantValue()?.type?.getDisplayString() == 'inject',
|
||||
);
|
||||
}
|
||||
|
||||
/// Parses the field for scope/named qualifiers and determines its type.
|
||||
/// Returns a [_ParsedInjectField] describing injection information.
|
||||
///
|
||||
/// Разбирает поле на наличие модификаторов scope/named и выясняет его тип.
|
||||
/// Возвращает [_ParsedInjectField] с информацией о внедрении.
|
||||
static _ParsedInjectField _parseInjectField(FieldElement field) {
|
||||
String? scopeName;
|
||||
String? namedValue;
|
||||
|
||||
for (final meta in field.metadata) {
|
||||
final DartObject? obj = meta.computeConstantValue();
|
||||
final type = obj?.type?.getDisplayString();
|
||||
if (type == 'scope') {
|
||||
scopeName = obj?.getField('name')?.toStringValue();
|
||||
} else if (type == 'named') {
|
||||
namedValue = obj?.getField('value')?.toStringValue();
|
||||
}
|
||||
}
|
||||
|
||||
final DartType dartType = field.type;
|
||||
String coreTypeName;
|
||||
bool isFuture;
|
||||
|
||||
if (dartType.isDartAsyncFuture) {
|
||||
final ParameterizedType paramType = dartType as ParameterizedType;
|
||||
coreTypeName = paramType.typeArguments.first.getDisplayString();
|
||||
isFuture = true;
|
||||
} else {
|
||||
coreTypeName = dartType.getDisplayString();
|
||||
isFuture = false;
|
||||
}
|
||||
|
||||
return _ParsedInjectField(
|
||||
fieldName: field.name,
|
||||
coreType: coreTypeName,
|
||||
isFuture: isFuture,
|
||||
scopeName: scopeName,
|
||||
namedValue: namedValue,
|
||||
);
|
||||
}
|
||||
|
||||
/// Generates a line of code that performs the dependency injection for a field.
|
||||
/// Handles resolve/resolveAsync, scoping, and named qualifiers.
|
||||
///
|
||||
/// Генерирует строку кода, которая внедряет зависимость для поля.
|
||||
/// Учитывает resolve/resolveAsync, scoping и named qualifier.
|
||||
String _generateInjectionLine(_ParsedInjectField field) {
|
||||
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}')"
|
||||
: '()';
|
||||
|
||||
return " instance.${field.fieldName} = $openCall.$methodName$params;";
|
||||
}
|
||||
}
|
||||
|
||||
/// Data structure representing all information required to generate
|
||||
/// injection code for a field.
|
||||
///
|
||||
/// Структура данных, содержащая всю информацию,
|
||||
/// необходимую для генерации кода внедрения для поля.
|
||||
class _ParsedInjectField {
|
||||
/// The name of the field / Имя поля.
|
||||
final String fieldName;
|
||||
|
||||
/// The base type name (T or Future<T>) / Базовый тип (T или тип из Future<T>).
|
||||
final String coreType;
|
||||
|
||||
/// True if the field type is Future<T>; false otherwise
|
||||
/// Истина, если поле — Future<T>, иначе — ложь.
|
||||
final bool isFuture;
|
||||
|
||||
/// Optional scope annotation argument / Опциональное имя scope.
|
||||
final String? scopeName;
|
||||
|
||||
/// Optional named annotation argument / Опциональное имя named.
|
||||
final String? namedValue;
|
||||
|
||||
_ParsedInjectField({
|
||||
required this.fieldName,
|
||||
required this.coreType,
|
||||
required this.isFuture,
|
||||
this.scopeName,
|
||||
this.namedValue,
|
||||
});
|
||||
}
|
||||
|
||||
/// Builder factory. Used by build_runner.
|
||||
///
|
||||
/// Фабрика билдера. Используется build_runner.
|
||||
Builder injectBuilder(BuilderOptions options) =>
|
||||
PartBuilder([InjectGenerator()], '.inject.cherrypick.g.dart');
|
||||
@@ -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');
|
||||
|
||||
@@ -2,7 +2,7 @@ name: cherrypick_generator
|
||||
description: |
|
||||
Source code generator for the cherrypick dependency injection system. Processes annotations to generate binding and module code for Dart & Flutter projects.
|
||||
|
||||
version: 1.1.0-dev.2
|
||||
version: 1.1.0-dev.3
|
||||
documentation: https://github.com/pese-git/cherrypick/wiki
|
||||
repository: https://github.com/pese-git/cherrypick/cherrypick_generator
|
||||
issue_tracker: https://github.com/pese-git/cherrypick/issues
|
||||
@@ -12,12 +12,13 @@ environment:
|
||||
|
||||
# Add regular dependencies here.
|
||||
dependencies:
|
||||
cherrypick_annotations: ^1.1.0-dev.0
|
||||
cherrypick_annotations: ^1.1.0-dev.1
|
||||
analyzer: ^6.7.0
|
||||
dart_style: ^2.3.7
|
||||
build: ^2.4.1
|
||||
build_runner: ^2.4.13
|
||||
source_gen: ^1.5.0
|
||||
collection: ^1.18.0
|
||||
|
||||
dev_dependencies:
|
||||
lints: ^5.0.0
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -14,7 +14,7 @@ dependencies:
|
||||
cherrypick: ^2.2.0-dev.1
|
||||
cherrypick_flutter: ^1.1.2-dev.1
|
||||
|
||||
cherrypick_annotations: ^1.1.0-dev.0
|
||||
cherrypick_annotations: ^1.1.0-dev.1
|
||||
|
||||
cupertino_icons: ^1.0.8
|
||||
|
||||
@@ -24,7 +24,7 @@ dev_dependencies:
|
||||
|
||||
flutter_lints: ^5.0.0
|
||||
|
||||
cherrypick_generator: ^1.1.0-dev.2
|
||||
cherrypick_generator: ^1.1.0-dev.3
|
||||
build_runner: ^2.4.13
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
|
||||
35
examples/postly/lib/app.dart
Normal file
35
examples/postly/lib/app.dart
Normal 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 _appRouter = AppRouter();
|
||||
|
||||
@named('repo')
|
||||
@inject()
|
||||
late final PostRepository repository;
|
||||
|
||||
MyApp({super.key}) {
|
||||
_inject(this);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (_) => PostBloc(repository),
|
||||
child: MaterialApp.router(
|
||||
routeInformationParser: _appRouter.defaultRouteParser(),
|
||||
routerDelegate: _appRouter.delegate(),
|
||||
theme: ThemeData.light(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -1,36 +1,9 @@
|
||||
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();
|
||||
scope.installModules([$AppModule()]);
|
||||
|
||||
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(),
|
||||
),
|
||||
);
|
||||
}
|
||||
CherryPick.openRootScope().installModules([$AppModule()]);
|
||||
runApp(MyApp());
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -13,7 +13,7 @@ dependencies:
|
||||
sdk: flutter
|
||||
|
||||
cherrypick: ^2.2.0-dev.1
|
||||
cherrypick_annotations: ^1.1.0-dev.0
|
||||
cherrypick_annotations: ^1.1.0-dev.1
|
||||
|
||||
dio: ^5.4.0
|
||||
retrofit: ^4.0.3
|
||||
@@ -30,7 +30,7 @@ dev_dependencies:
|
||||
|
||||
flutter_lints: ^5.0.0
|
||||
|
||||
cherrypick_generator: ^1.1.0-dev.2
|
||||
cherrypick_generator: ^1.1.0-dev.3
|
||||
build_runner: 2.4.13
|
||||
|
||||
retrofit_generator: ^9.1.5
|
||||
|
||||
Reference in New Issue
Block a user