Compare commits

...

9 Commits

Author SHA1 Message Date
Sergey Penkovsky
0651f74472 chore(release): publish packages
- cherrypick_annotations@3.0.2
 - cherrypick_generator@3.0.2
2026-01-29 16:58:46 +03:00
Sergey Penkovsky
776f29945a chore: format code and update deps 2026-01-29 16:57:28 +03:00
Sergey Penkovsky
49361f2f9e Merge pull request #27 from pese-git/develop
Develop
2026-01-29 16:41:03 +03:00
Sergey Penkovsky
4953e917c9 Merge branch 'master' into develop 2026-01-15 09:00:10 +03:00
Sergey Penkovsky
1997110d92 chore(release): publish packages
- cherrypick@3.0.2
 - cherrypick_flutter@3.0.2
 - talker_cherrypick_logger@3.0.2
2025-10-20 17:31:40 +03:00
Sergey Penkovsky
0e600ca3a2 Merge pull request #25 from pese-git/issues/24
Issues/24
2025-10-20 17:28:01 +03:00
Sergey Penkovsky
25ae208ea1 fix(test): fix warning 2025-10-20 16:20:20 +03:00
Sergey Penkovsky
685c0ae49c fix(scope): properly clear binding and module references on dispose
Add memory leak/finalizer test to ensure no strong references remain after closing and disposing a scope.
2025-10-13 17:32:10 +03:00
Sergey Penkovsky
98d81b13a8 freeze deps 2025-10-13 17:26:39 +03:00
21 changed files with 191 additions and 48 deletions

View File

@@ -3,6 +3,64 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## 2026-01-29
### Changes
---
Packages with breaking changes:
- There are no breaking changes in this release.
Packages with other changes:
- [`cherrypick_annotations` - `v3.0.2`](#cherrypick_annotations---v302)
- [`cherrypick_generator` - `v3.0.2`](#cherrypick_generator---v302)
Packages graduated to a stable release (see pre-releases prior to the stable version for changelog entries):
- `cherrypick_annotations` - `v3.0.2`
- `cherrypick_generator` - `v3.0.2`
---
#### `cherrypick_annotations` - `v3.0.2`
#### `cherrypick_generator` - `v3.0.2`
## 2025-10-20
### Changes
---
Packages with breaking changes:
- There are no breaking changes in this release.
Packages with other changes:
- [`cherrypick` - `v3.0.2`](#cherrypick---v302)
- [`cherrypick_flutter` - `v3.0.2`](#cherrypick_flutter---v302)
- [`talker_cherrypick_logger` - `v3.0.2`](#talker_cherrypick_logger---v302)
Packages with dependency updates only:
> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project.
- `cherrypick_flutter` - `v3.0.2`
- `talker_cherrypick_logger` - `v3.0.2`
---
#### `cherrypick` - `v3.0.2`
- **FIX**(test): fix warning.
- **FIX**(scope): properly clear binding and module references on dispose.
## 2025-09-09 ## 2025-09-09
### Changes ### Changes

View File

@@ -47,7 +47,7 @@ packages:
path: "../cherrypick" path: "../cherrypick"
relative: true relative: true
source: path source: path
version: "3.0.0" version: "3.0.2"
collection: collection:
dependency: transitive dependency: transitive
description: description:

View File

@@ -1,3 +1,8 @@
## 3.0.2
- **FIX**(test): fix warning.
- **FIX**(scope): properly clear binding and module references on dispose.
## 3.0.1 ## 3.0.1
- **DOCS**: add Netlify deployment status badge to README files. - **DOCS**: add Netlify deployment status badge to README files.

View File

@@ -498,5 +498,10 @@ class Scope with CycleDetectionMixin, GlobalCycleDetectionMixin {
await d.dispose(); await d.dispose();
} }
_disposables.clear(); _disposables.clear();
// Clear modules
_modulesList.clear();
// Clear binding-index
_bindingResolvers.clear();
} }
} }

View File

@@ -1,6 +1,6 @@
name: cherrypick name: cherrypick
description: Cherrypick is a small dependency injection (DI) library for dart/flutter projects. description: Cherrypick is a small dependency injection (DI) library for dart/flutter projects.
version: 3.0.1 version: 3.0.2
homepage: https://cherrypick-di.netlify.app homepage: https://cherrypick-di.netlify.app
documentation: https://cherrypick-di.netlify.app/docs/intro documentation: https://cherrypick-di.netlify.app/docs/intro
repository: https://github.com/pese-git/cherrypick repository: https://github.com/pese-git/cherrypick

View File

@@ -0,0 +1,65 @@
import 'dart:async';
import 'package:cherrypick/cherrypick.dart';
import 'package:test/test.dart';
class HeavyService implements Disposable {
static int instanceCount = 0;
HeavyService() {
instanceCount++;
print('HeavyService created. Instance count: '
'\u001b[32m$instanceCount\u001b[0m');
}
@override
void dispose() {
instanceCount--;
print('HeavyService disposed. Instance count: '
'\u001b[31m$instanceCount\u001b[0m');
}
static final Finalizer<String> _finalizer = Finalizer((msg) {
print('GC FINALIZED HeavyService: $msg');
});
void registerFinalizer() => _finalizer.attach(this, toString(), detach: this);
}
class HeavyModule extends Module {
@override
void builder(Scope scope) {
bind<HeavyService>().toProvide(() => HeavyService());
}
}
void main() {
test('Binding memory is cleared after closing and reopening scope', () async {
final root = CherryPick.openRootScope();
for (int i = 0; i < 10; i++) {
print('\nIteration $i -------------------------------');
final subScope = root.openSubScope('leak-test-scope');
subScope.installModules([HeavyModule()]);
final service = subScope.resolve<HeavyService>();
expect(service, isNotNull);
await root.closeSubScope('leak-test-scope');
// Dart GC не сразу удаляет освобождённые объекты, добавляем паузу и вызываем GC.
await Future.delayed(const Duration(milliseconds: 200));
}
// Если dispose не вызвался, instanceCount > 0 => утечка.
expect(HeavyService.instanceCount, equals(0));
});
test('Service is finalized after scope is closed/cleaned', () async {
final root = CherryPick.openRootScope();
HeavyService? ref;
{
final sub = root.openSubScope('s');
sub.installModules([HeavyModule()]);
ref = sub.resolve<HeavyService>();
ref.registerFinalizer();
expect(HeavyService.instanceCount, 1);
await root.closeSubScope('s');
}
await Future.delayed(const Duration(seconds: 2));
expect(HeavyService.instanceCount, 0);
});
}

View File

@@ -1,3 +1,7 @@
## 3.0.2
- Graduate package to a stable release. See pre-releases prior to this version for changelog entries.
## 3.0.2-dev.0 ## 3.0.2-dev.0
- **REFACTOR**(generator): migrate cherrypick_generator to analyzer element2 API. - **REFACTOR**(generator): migrate cherrypick_generator to analyzer element2 API.

View File

@@ -1,7 +1,7 @@
name: cherrypick_annotations name: cherrypick_annotations
description: | description: |
Set of annotations for CherryPick dependency injection library. Enables code generation and declarative DI for Dart & Flutter projects. Set of annotations for CherryPick dependency injection library. Enables code generation and declarative DI for Dart & Flutter projects.
version: 3.0.2-dev.0 version: 3.0.2
homepage: https://cherrypick-di.netlify.app homepage: https://cherrypick-di.netlify.app
documentation: https://cherrypick-di.netlify.app/docs/intro documentation: https://cherrypick-di.netlify.app/docs/intro
repository: https://github.com/pese-git/cherrypick/cherrypick_annotations repository: https://github.com/pese-git/cherrypick/cherrypick_annotations

View File

@@ -1,3 +1,7 @@
## 3.0.2
- Update a dependency to the latest release.
## 3.0.1 ## 3.0.1
- **DOCS**: add Netlify deployment status badge to README files. - **DOCS**: add Netlify deployment status badge to README files.

View File

@@ -1,6 +1,6 @@
name: cherrypick_flutter name: cherrypick_flutter
description: "Flutter library that allows access to the root scope through the context using `CherryPickProvider`." description: "Flutter library that allows access to the root scope through the context using `CherryPickProvider`."
version: 3.0.1 version: 3.0.2
homepage: https://cherrypick-di.netlify.app homepage: https://cherrypick-di.netlify.app
documentation: https://cherrypick-di.netlify.app/docs/intro documentation: https://cherrypick-di.netlify.app/docs/intro
repository: https://github.com/pese-git/cherrypick repository: https://github.com/pese-git/cherrypick
@@ -19,7 +19,7 @@ environment:
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
cherrypick: ^3.0.1 cherrypick: ^3.0.2
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

View File

@@ -1,3 +1,7 @@
## 3.0.2
- Graduate package to a stable release. See pre-releases prior to this version for changelog entries.
## 3.0.2-dev.0 ## 3.0.2-dev.0
- **REFACTOR**(generator): migrate cherrypick_generator to analyzer element2 API. - **REFACTOR**(generator): migrate cherrypick_generator to analyzer element2 API.

View File

@@ -179,10 +179,10 @@ class InjectGenerator extends GeneratorForAnnotation<ann.injectable> {
// Determine nullability for field types like T? or Future<T?> // Determine nullability for field types like T? or Future<T?>
bool isNullable = bool isNullable =
dartType.nullabilitySuffix == NullabilitySuffix.question || dartType.nullabilitySuffix == NullabilitySuffix.question ||
(dartType is ParameterizedType && (dartType is ParameterizedType &&
(dartType).typeArguments.any( (dartType).typeArguments.any(
(t) => t.nullabilitySuffix == NullabilitySuffix.question, (t) => t.nullabilitySuffix == NullabilitySuffix.question,
)); ));
return _ParsedInjectField( return _ParsedInjectField(
fieldName: field.firstFragment.name2 ?? '', fieldName: field.firstFragment.name2 ?? '',
@@ -207,11 +207,11 @@ class InjectGenerator extends GeneratorForAnnotation<ann.injectable> {
String _generateInjectionLine(_ParsedInjectField field) { String _generateInjectionLine(_ParsedInjectField field) {
final resolveMethod = field.isFuture final resolveMethod = field.isFuture
? (field.isNullable ? (field.isNullable
? 'tryResolveAsync<${field.coreType}>' ? 'tryResolveAsync<${field.coreType}>'
: 'resolveAsync<${field.coreType}>') : 'resolveAsync<${field.coreType}>')
: (field.isNullable : (field.isNullable
? 'tryResolve<${field.coreType}>' ? 'tryResolve<${field.coreType}>'
: 'resolve<${field.coreType}>'); : 'resolve<${field.coreType}>');
final openCall = (field.scopeName != null && field.scopeName!.isNotEmpty) final openCall = (field.scopeName != null && field.scopeName!.isNotEmpty)
? "CherryPick.openScope(scopeName: '${field.scopeName}')" ? "CherryPick.openScope(scopeName: '${field.scopeName}')"

View File

@@ -305,9 +305,8 @@ class AnnotationValidator {
} }
// Check if class has public methods // Check if class has public methods
final publicMethods = classElement.methods2 final publicMethods =
.where((m) => m.isPublic) classElement.methods2.where((m) => m.isPublic).toList();
.toList();
if (publicMethods.isEmpty) { if (publicMethods.isEmpty) {
throw AnnotationValidationException( throw AnnotationValidationException(
'Module class must have at least one public method', 'Module class must have at least one public method',

View File

@@ -190,24 +190,20 @@ class BindSpec {
case BindingType.provide: case BindingType.provide:
if (isAsyncProvide) { if (isAsyncProvide) {
if (needsMultiline) { if (needsMultiline) {
final lambdaIndent = (isSingleton || named != null) final lambdaIndent =
? indent + 6 (isSingleton || named != null) ? indent + 6 : indent + 2;
: indent + 2; final closingIndent =
final closingIndent = (isSingleton || named != null) (isSingleton || named != null) ? indent + 4 : indent;
? indent + 4
: indent;
return '.toProvideAsync(\n${' ' * lambdaIndent}() => $methodName($argsStr),\n${' ' * closingIndent})'; return '.toProvideAsync(\n${' ' * lambdaIndent}() => $methodName($argsStr),\n${' ' * closingIndent})';
} else { } else {
return '.toProvideAsync(() => $methodName($argsStr))'; return '.toProvideAsync(() => $methodName($argsStr))';
} }
} else { } else {
if (needsMultiline) { if (needsMultiline) {
final lambdaIndent = (isSingleton || named != null) final lambdaIndent =
? indent + 6 (isSingleton || named != null) ? indent + 6 : indent + 2;
: indent + 2; final closingIndent =
final closingIndent = (isSingleton || named != null) (isSingleton || named != null) ? indent + 4 : indent;
? indent + 4
: indent;
return '.toProvide(\n${' ' * lambdaIndent}() => $methodName($argsStr),\n${' ' * closingIndent})'; return '.toProvide(\n${' ' * lambdaIndent}() => $methodName($argsStr),\n${' ' * closingIndent})';
} else { } else {
return '.toProvide(() => $methodName($argsStr))'; return '.toProvide(() => $methodName($argsStr))';
@@ -311,9 +307,8 @@ class BindSpec {
); );
} }
final bindingType = hasInstance final bindingType =
? BindingType.instance hasInstance ? BindingType.instance : BindingType.provide;
: BindingType.provide;
// PROHIBIT @params with @instance bindings! // PROHIBIT @params with @instance bindings!
if (bindingType == BindingType.instance && hasParams) { if (bindingType == BindingType.instance && hasParams) {

View File

@@ -53,9 +53,9 @@ class CherryPickGeneratorException extends InvalidGenerationSourceError {
this.suggestion, this.suggestion,
this.context, this.context,
}) : super( }) : super(
_formatMessage(message, category, suggestion, context, element), _formatMessage(message, category, suggestion, context, element),
element: element, element: element,
); );
static String _formatMessage( static String _formatMessage(
String message, String message,

View File

@@ -2,7 +2,7 @@ name: cherrypick_generator
description: | description: |
Source code generator for the cherrypick dependency injection system. Processes annotations to generate binding and module code for Dart & Flutter projects. Source code generator for the cherrypick dependency injection system. Processes annotations to generate binding and module code for Dart & Flutter projects.
version: 3.0.2-dev.0 version: 3.0.2
homepage: https://cherrypick-di.netlify.app homepage: https://cherrypick-di.netlify.app
documentation: https://cherrypick-di.netlify.app/docs/intro documentation: https://cherrypick-di.netlify.app/docs/intro
repository: https://github.com/pese-git/cherrypick/cherrypick_generator repository: https://github.com/pese-git/cherrypick/cherrypick_generator
@@ -19,7 +19,7 @@ environment:
# Add regular dependencies here. # Add regular dependencies here.
dependencies: dependencies:
cherrypick_annotations: ^3.0.2-dev.0 cherrypick_annotations: ^3.0.2
analyzer: ">=7.5.9 <8.0.0" analyzer: ">=7.5.9 <8.0.0"
dart_style: ^3.0.0 dart_style: ^3.0.0
build: ^3.0.0 build: ^3.0.0

View File

@@ -127,28 +127,28 @@ packages:
path: "../../cherrypick" path: "../../cherrypick"
relative: true relative: true
source: path source: path
version: "3.0.0" version: "3.0.2"
cherrypick_annotations: cherrypick_annotations:
dependency: "direct main" dependency: "direct main"
description: description:
path: "../../cherrypick_annotations" path: "../../cherrypick_annotations"
relative: true relative: true
source: path source: path
version: "3.0.0" version: "3.0.2-dev.0"
cherrypick_flutter: cherrypick_flutter:
dependency: "direct main" dependency: "direct main"
description: description:
path: "../../cherrypick_flutter" path: "../../cherrypick_flutter"
relative: true relative: true
source: path source: path
version: "3.0.0" version: "3.0.2"
cherrypick_generator: cherrypick_generator:
dependency: "direct dev" dependency: "direct dev"
description: description:
path: "../../cherrypick_generator" path: "../../cherrypick_generator"
relative: true relative: true
source: path source: path
version: "3.0.0" version: "3.0.2-dev.0"
clock: clock:
dependency: transitive dependency: transitive
description: description:

View File

@@ -175,21 +175,21 @@ packages:
path: "../../cherrypick" path: "../../cherrypick"
relative: true relative: true
source: path source: path
version: "3.0.0" version: "3.0.2"
cherrypick_annotations: cherrypick_annotations:
dependency: "direct main" dependency: "direct main"
description: description:
path: "../../cherrypick_annotations" path: "../../cherrypick_annotations"
relative: true relative: true
source: path source: path
version: "3.0.0" version: "3.0.2-dev.0"
cherrypick_generator: cherrypick_generator:
dependency: "direct dev" dependency: "direct dev"
description: description:
path: "../../cherrypick_generator" path: "../../cherrypick_generator"
relative: true relative: true
source: path source: path
version: "3.0.0" version: "3.0.2-dev.0"
cli_config: cli_config:
dependency: transitive dependency: transitive
description: description:
@@ -920,7 +920,7 @@ packages:
path: "../../talker_cherrypick_logger" path: "../../talker_cherrypick_logger"
relative: true relative: true
source: path source: path
version: "3.0.0" version: "3.0.2"
talker_dio_logger: talker_dio_logger:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@@ -13,7 +13,7 @@ dependencies:
sdk: flutter sdk: flutter
cherrypick: any cherrypick: any
cherrypick_annotations: ^3.0.2-dev.0 cherrypick_annotations: ^3.0.2
dio: ^5.4.0 dio: ^5.4.0
retrofit: ^4.0.3 retrofit: ^4.0.3
@@ -38,7 +38,7 @@ dev_dependencies:
flutter_lints: ^6.0.0 flutter_lints: ^6.0.0
build_runner: ^2.5.0 build_runner: ^2.5.0
cherrypick_generator: ^3.0.2-dev.0 cherrypick_generator: ^3.0.2
json_serializable: ^6.9.0 json_serializable: ^6.9.0
retrofit_generator: ^10.0.5 retrofit_generator: ^10.0.5

View File

@@ -1,3 +1,7 @@
## 3.0.2
- Update a dependency to the latest release.
## 3.0.1 ## 3.0.1
- **DOCS**: add Netlify deployment status badge to README files. - **DOCS**: add Netlify deployment status badge to README files.

View File

@@ -1,6 +1,6 @@
name: talker_cherrypick_logger name: talker_cherrypick_logger
description: A Talker logger integration for CherryPick DI to observe and log DI events and errors. description: A Talker logger integration for CherryPick DI to observe and log DI events and errors.
version: 3.0.1 version: 3.0.2
homepage: https://cherrypick-di.netlify.app homepage: https://cherrypick-di.netlify.app
documentation: https://cherrypick-di.netlify.app/docs/intro documentation: https://cherrypick-di.netlify.app/docs/intro
repository: https://github.com/pese-git/cherrypick repository: https://github.com/pese-git/cherrypick
@@ -18,7 +18,7 @@ environment:
# Add regular dependencies here. # Add regular dependencies here.
dependencies: dependencies:
talker: ^5.0.0 talker: ^5.0.0
cherrypick: ^3.0.1 cherrypick: ^3.0.2
dev_dependencies: dev_dependencies: