mirror of
https://github.com/pese-git/cherrypick.git
synced 2026-03-24 20:12:04 +00:00
Compare commits
1 Commits
ref/code_b
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3331a3ee9c |
@@ -11,17 +11,14 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import 'package:analyzer/dart/constant/value.dart';
|
||||||
import 'package:analyzer/dart/element/element2.dart';
|
import 'package:analyzer/dart/element/element2.dart';
|
||||||
|
import 'package:analyzer/dart/element/nullability_suffix.dart';
|
||||||
import 'package:analyzer/dart/element/type.dart';
|
import 'package:analyzer/dart/element/type.dart';
|
||||||
import 'package:build/build.dart';
|
import 'package:build/build.dart';
|
||||||
import 'package:code_builder/code_builder.dart';
|
|
||||||
import 'package:source_gen/source_gen.dart';
|
import 'package:source_gen/source_gen.dart';
|
||||||
import 'package:cherrypick_annotations/cherrypick_annotations.dart' as ann;
|
import 'package:cherrypick_annotations/cherrypick_annotations.dart' as ann;
|
||||||
|
|
||||||
import 'src/annotation_validator.dart';
|
|
||||||
import 'src/code_builder_emitters.dart';
|
|
||||||
import 'src/type_parser.dart';
|
|
||||||
|
|
||||||
/// CherryPick DI field injector generator for codegen.
|
/// CherryPick DI field injector generator for codegen.
|
||||||
///
|
///
|
||||||
/// Analyzes all Dart classes marked with `@injectable()` and generates a mixin (for example, `_$ProfileScreen`)
|
/// Analyzes all Dart classes marked with `@injectable()` and generates a mixin (for example, `_$ProfileScreen`)
|
||||||
@@ -118,54 +115,24 @@ class InjectGenerator extends GeneratorForAnnotation<ann.injectable> {
|
|||||||
final className = classElement.firstFragment.name2;
|
final className = classElement.firstFragment.name2;
|
||||||
final mixinName = '_\$$className';
|
final mixinName = '_\$$className';
|
||||||
|
|
||||||
AnnotationValidator.validateClassAnnotations(classElement);
|
final buffer = StringBuffer()
|
||||||
|
..writeln('mixin $mixinName {')
|
||||||
final classType = TypeParser.parseType(classElement.thisType, classElement);
|
..writeln(' void _inject($className instance) {');
|
||||||
|
|
||||||
|
// Collect and process all @inject fields
|
||||||
final injectFields = classElement.fields2
|
final injectFields = classElement.fields2
|
||||||
.where((f) => _isInjectField(f))
|
.where((f) => _isInjectField(f))
|
||||||
.map(_parseInjectField)
|
.map((f) => _parseInjectField(f));
|
||||||
.toList();
|
|
||||||
|
|
||||||
final injectMethod = Method((b) {
|
for (final parsedField in injectFields) {
|
||||||
b
|
buffer.writeln(_generateInjectionLine(parsedField));
|
||||||
..name = '_inject'
|
}
|
||||||
..returns = refer('void')
|
|
||||||
..requiredParameters.add(
|
|
||||||
Parameter((p) {
|
|
||||||
p
|
|
||||||
..name = 'instance'
|
|
||||||
..type = CodeBuilderEmitters.resolveTypeRef(classType);
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
..body = Block((body) {
|
|
||||||
for (final field in injectFields) {
|
|
||||||
final scopeExpr = CodeBuilderEmitters.openScope(
|
|
||||||
scopeName: field.scopeName,
|
|
||||||
);
|
|
||||||
final resolveExpr = CodeBuilderEmitters.resolveCall(
|
|
||||||
scopeExpr: scopeExpr,
|
|
||||||
parsedType: field.parsedType,
|
|
||||||
named: field.namedValue,
|
|
||||||
);
|
|
||||||
body.statements.add(
|
|
||||||
refer(
|
|
||||||
'instance',
|
|
||||||
).property(field.fieldName).assign(resolveExpr).statement,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
final mixin = Mixin((b) {
|
buffer
|
||||||
b
|
..writeln(' }')
|
||||||
..name = mixinName
|
..writeln('}');
|
||||||
..methods.add(injectMethod);
|
|
||||||
});
|
|
||||||
|
|
||||||
final library = Library((b) => b..body.add(mixin));
|
return buffer.toString();
|
||||||
final emitter = DartEmitter(useNullSafetySyntax: true);
|
|
||||||
return '${library.accept(emitter)}';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if a field is annotated with `@inject`.
|
/// Returns true if a field is annotated with `@inject`.
|
||||||
@@ -183,13 +150,11 @@ class InjectGenerator extends GeneratorForAnnotation<ann.injectable> {
|
|||||||
/// Converts Dart field declaration and all parameterizing injection-related
|
/// Converts Dart field declaration and all parameterizing injection-related
|
||||||
/// annotations into a [_ParsedInjectField] which is used for codegen.
|
/// annotations into a [_ParsedInjectField] which is used for codegen.
|
||||||
static _ParsedInjectField _parseInjectField(FieldElement2 field) {
|
static _ParsedInjectField _parseInjectField(FieldElement2 field) {
|
||||||
AnnotationValidator.validateFieldAnnotations(field);
|
|
||||||
|
|
||||||
String? scopeName;
|
String? scopeName;
|
||||||
String? namedValue;
|
String? namedValue;
|
||||||
|
|
||||||
for (final meta in field.firstFragment.metadata2.annotations) {
|
for (final meta in field.firstFragment.metadata2.annotations) {
|
||||||
final obj = meta.computeConstantValue();
|
final DartObject? obj = meta.computeConstantValue();
|
||||||
final type = obj?.type?.getDisplayString();
|
final type = obj?.type?.getDisplayString();
|
||||||
if (type == 'scope') {
|
if (type == 'scope') {
|
||||||
scopeName = obj?.getField('name')?.toStringValue();
|
scopeName = obj?.getField('name')?.toStringValue();
|
||||||
@@ -199,15 +164,65 @@ class InjectGenerator extends GeneratorForAnnotation<ann.injectable> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final DartType dartType = field.type;
|
final DartType dartType = field.type;
|
||||||
final parsedType = TypeParser.parseType(dartType, field);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine nullability for field types like T? or Future<T?>
|
||||||
|
bool isNullable =
|
||||||
|
dartType.nullabilitySuffix == NullabilitySuffix.question ||
|
||||||
|
(dartType is ParameterizedType &&
|
||||||
|
(dartType).typeArguments.any(
|
||||||
|
(t) => t.nullabilitySuffix == NullabilitySuffix.question,
|
||||||
|
));
|
||||||
|
|
||||||
return _ParsedInjectField(
|
return _ParsedInjectField(
|
||||||
fieldName: field.firstFragment.name2 ?? '',
|
fieldName: field.firstFragment.name2 ?? '',
|
||||||
parsedType: parsedType,
|
coreType: coreTypeName.replaceAll('?', ''), // удаляем "?" на всякий
|
||||||
|
isFuture: isFuture,
|
||||||
|
isNullable: isNullable,
|
||||||
scopeName: scopeName,
|
scopeName: scopeName,
|
||||||
namedValue: namedValue,
|
namedValue: namedValue,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generates Dart code for a single dependency-injected field based on its metadata.
|
||||||
|
///
|
||||||
|
/// This code will resolve the field from the CherryPick DI container and assign it to the class instance.
|
||||||
|
/// Correctly dispatches to resolve, tryResolve, resolveAsync, or tryResolveAsync methods,
|
||||||
|
/// and applies container scoping or named resolution where required.
|
||||||
|
///
|
||||||
|
/// Returns literal Dart code as string (1 line).
|
||||||
|
///
|
||||||
|
/// Example output:
|
||||||
|
/// `instance.logger = CherryPick.openRootScope().resolve<Logger>();`
|
||||||
|
String _generateInjectionLine(_ParsedInjectField field) {
|
||||||
|
final resolveMethod = field.isFuture
|
||||||
|
? (field.isNullable
|
||||||
|
? 'tryResolveAsync<${field.coreType}>'
|
||||||
|
: 'resolveAsync<${field.coreType}>')
|
||||||
|
: (field.isNullable
|
||||||
|
? 'tryResolve<${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.$resolveMethod$params;";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Internal structure: describes all required information for generating the injection
|
/// Internal structure: describes all required information for generating the injection
|
||||||
@@ -218,8 +233,14 @@ class _ParsedInjectField {
|
|||||||
/// The name of the field to be injected.
|
/// The name of the field to be injected.
|
||||||
final String fieldName;
|
final String fieldName;
|
||||||
|
|
||||||
/// Parsed type info for the field.
|
/// The Dart type to resolve (e.g. `Logger` from `Logger` or `Future<Logger>`).
|
||||||
final ParsedType parsedType;
|
final String coreType;
|
||||||
|
|
||||||
|
/// True if the field is an async dependency (Future<...>), otherwise false.
|
||||||
|
final bool isFuture;
|
||||||
|
|
||||||
|
/// True if the field accepts null (T?), otherwise false.
|
||||||
|
final bool isNullable;
|
||||||
|
|
||||||
/// The scoping for DI resolution, or null to use root scope.
|
/// The scoping for DI resolution, or null to use root scope.
|
||||||
final String? scopeName;
|
final String? scopeName;
|
||||||
@@ -229,7 +250,9 @@ class _ParsedInjectField {
|
|||||||
|
|
||||||
_ParsedInjectField({
|
_ParsedInjectField({
|
||||||
required this.fieldName,
|
required this.fieldName,
|
||||||
required this.parsedType,
|
required this.coreType,
|
||||||
|
required this.isFuture,
|
||||||
|
required this.isNullable,
|
||||||
this.scopeName,
|
this.scopeName,
|
||||||
this.namedValue,
|
this.namedValue,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,80 +0,0 @@
|
|||||||
//
|
|
||||||
// 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
|
|
||||||
// https://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:code_builder/code_builder.dart';
|
|
||||||
|
|
||||||
import 'type_parser.dart';
|
|
||||||
|
|
||||||
/// Small helpers for building code_builder AST nodes used by generators.
|
|
||||||
class CodeBuilderEmitters {
|
|
||||||
/// Builds a CherryPick scope opener expression.
|
|
||||||
///
|
|
||||||
/// - If [scopeName] is empty or null, uses openRootScope().
|
|
||||||
/// - Otherwise uses openScope(scopeName: ...).
|
|
||||||
static Expression openScope({String? scopeName}) {
|
|
||||||
if (scopeName == null || scopeName.isEmpty) {
|
|
||||||
return refer('CherryPick').property('openRootScope').call([]);
|
|
||||||
}
|
|
||||||
return refer(
|
|
||||||
'CherryPick',
|
|
||||||
).property('openScope').call([], {'scopeName': literalString(scopeName)});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Builds a TypeReference appropriate for resolving a dependency.
|
|
||||||
///
|
|
||||||
/// For Future<T>, it returns the inner type reference (T).
|
|
||||||
/// Nullability and generic arguments are preserved.
|
|
||||||
static TypeReference resolveTypeRef(ParsedType parsedType) {
|
|
||||||
final target = parsedType.isFuture && parsedType.innerType != null
|
|
||||||
? parsedType.innerType!
|
|
||||||
: parsedType;
|
|
||||||
return _typeRefFromParsedType(target, stripNullability: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Builds a DI resolve call on [scopeExpr] using [parsedType] and [named].
|
|
||||||
///
|
|
||||||
/// The method name is derived from [ParsedType.resolveMethodName].
|
|
||||||
static Expression resolveCall({
|
|
||||||
required Expression scopeExpr,
|
|
||||||
required ParsedType parsedType,
|
|
||||||
String? named,
|
|
||||||
}) {
|
|
||||||
final typeRef = resolveTypeRef(parsedType);
|
|
||||||
final method = parsedType.resolveMethodName;
|
|
||||||
final args = <Expression>[];
|
|
||||||
final namedArgs = <String, Expression>{};
|
|
||||||
if (named != null && named.isNotEmpty) {
|
|
||||||
namedArgs['named'] = literalString(named);
|
|
||||||
}
|
|
||||||
return scopeExpr.property(method).call(args, namedArgs, [typeRef]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static TypeReference _typeRefFromParsedType(
|
|
||||||
ParsedType parsedType, {
|
|
||||||
required bool stripNullability,
|
|
||||||
}) {
|
|
||||||
return TypeReference((b) {
|
|
||||||
b
|
|
||||||
..symbol = parsedType.coreType
|
|
||||||
..isNullable = stripNullability ? false : parsedType.isNullable;
|
|
||||||
if (parsedType.typeArguments.isNotEmpty) {
|
|
||||||
b.types.addAll(
|
|
||||||
parsedType.typeArguments.map(
|
|
||||||
(arg) =>
|
|
||||||
_typeRefFromParsedType(arg, stripNullability: stripNullability),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -65,8 +65,6 @@ class CherryPickGeneratorException extends InvalidGenerationSourceError {
|
|||||||
Element2 element,
|
Element2 element,
|
||||||
) {
|
) {
|
||||||
final buffer = StringBuffer();
|
final buffer = StringBuffer();
|
||||||
final location = _safeLocation(element);
|
|
||||||
final enclosing = _safeEnclosingDisplayName(element);
|
|
||||||
|
|
||||||
// Header with category
|
// Header with category
|
||||||
buffer.writeln('[$category] $message');
|
buffer.writeln('[$category] $message');
|
||||||
@@ -76,11 +74,18 @@ class CherryPickGeneratorException extends InvalidGenerationSourceError {
|
|||||||
buffer.writeln('Context:');
|
buffer.writeln('Context:');
|
||||||
buffer.writeln(' Element: ${element.displayName}');
|
buffer.writeln(' Element: ${element.displayName}');
|
||||||
buffer.writeln(' Type: ${element.runtimeType}');
|
buffer.writeln(' Type: ${element.runtimeType}');
|
||||||
buffer.writeln(' Location: $location');
|
buffer.writeln(
|
||||||
|
' Location: ${element.firstFragment.libraryFragment?.source.fullName ?? 'unknown'}',
|
||||||
|
);
|
||||||
|
|
||||||
// Try to show enclosing element info for extra context
|
// Try to show enclosing element info for extra context
|
||||||
if (enclosing != null) {
|
try {
|
||||||
buffer.writeln(' Enclosing: $enclosing');
|
final enclosing = (element as dynamic).enclosingElement;
|
||||||
|
if (enclosing != null) {
|
||||||
|
buffer.writeln(' Enclosing: ${enclosing.displayName}');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Ignore if enclosingElement is not available
|
||||||
}
|
}
|
||||||
|
|
||||||
// Arbitrary user context
|
// Arbitrary user context
|
||||||
@@ -100,34 +105,6 @@ class CherryPickGeneratorException extends InvalidGenerationSourceError {
|
|||||||
|
|
||||||
return buffer.toString();
|
return buffer.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Best-effort extraction of element location for diagnostics.
|
|
||||||
///
|
|
||||||
/// Some tests use lightweight mocks for [Element2] that don't implement
|
|
||||||
/// analyzer fragment APIs, so this method must never throw.
|
|
||||||
static String _safeLocation(Element2 element) {
|
|
||||||
try {
|
|
||||||
return element.firstFragment.libraryFragment?.source.fullName ??
|
|
||||||
'unknown';
|
|
||||||
} catch (_) {
|
|
||||||
return 'unknown';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Best-effort extraction of enclosing element display name.
|
|
||||||
///
|
|
||||||
/// Accessed via dynamic to stay compatible with analyzer API differences.
|
|
||||||
static String? _safeEnclosingDisplayName(Element2 element) {
|
|
||||||
try {
|
|
||||||
final enclosing = (element as dynamic).enclosingElement;
|
|
||||||
if (enclosing == null) return null;
|
|
||||||
final name = enclosing.displayName;
|
|
||||||
if (name is String && name.isNotEmpty) return name;
|
|
||||||
return enclosing.toString();
|
|
||||||
} catch (_) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ---------------------------------------------------------------------------
|
/// ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ dependencies:
|
|||||||
cherrypick_annotations: ^4.0.0-dev.0
|
cherrypick_annotations: ^4.0.0-dev.0
|
||||||
analyzer: ">=8.2.9 <10.0.1"
|
analyzer: ">=8.2.9 <10.0.1"
|
||||||
dart_style: ^3.0.0
|
dart_style: ^3.0.0
|
||||||
code_builder: ^4.10.1
|
|
||||||
build: ^3.0.0
|
build: ^3.0.0
|
||||||
source_gen: ^4.2.0
|
source_gen: ^4.2.0
|
||||||
collection: ^1.18.0
|
collection: ^1.18.0
|
||||||
|
|||||||
32
cherrypick_generator/test/cherrypick_generator_test.dart
Normal file
32
cherrypick_generator/test/cherrypick_generator_test.dart
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
//
|
||||||
|
// 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
|
||||||
|
// https://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:test/test.dart';
|
||||||
|
|
||||||
|
// Import working test suites
|
||||||
|
import 'simple_test.dart' as simple_tests;
|
||||||
|
import 'bind_spec_test.dart' as bind_spec_tests;
|
||||||
|
import 'metadata_utils_test.dart' as metadata_utils_tests;
|
||||||
|
// Import integration test suites (now working!)
|
||||||
|
import 'module_generator_test.dart' as module_generator_tests;
|
||||||
|
import 'inject_generator_test.dart' as inject_generator_tests;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('CherryPick Generator Tests', () {
|
||||||
|
group('Simple Tests', simple_tests.main);
|
||||||
|
group('BindSpec Tests', bind_spec_tests.main);
|
||||||
|
group('MetadataUtils Tests', metadata_utils_tests.main);
|
||||||
|
group('ModuleGenerator Tests', module_generator_tests.main);
|
||||||
|
group('InjectGenerator Tests', inject_generator_tests.main);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -11,13 +11,9 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import 'dart:isolate';
|
|
||||||
|
|
||||||
import 'package:build/build.dart';
|
import 'package:build/build.dart';
|
||||||
import 'package:build_test/build_test.dart';
|
import 'package:build_test/build_test.dart';
|
||||||
import 'package:build_runner_core/build_runner_core.dart';
|
|
||||||
import 'package:cherrypick_generator/inject_generator.dart';
|
import 'package:cherrypick_generator/inject_generator.dart';
|
||||||
import 'package:package_config/package_config.dart';
|
|
||||||
import 'package:source_gen/source_gen.dart';
|
import 'package:source_gen/source_gen.dart';
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
@@ -44,8 +40,8 @@ class TestWidget {
|
|||||||
''';
|
''';
|
||||||
|
|
||||||
const expectedOutput = '''
|
const expectedOutput = '''
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// dart format width=80
|
// dart format width=80
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'test_widget.dart';
|
part of 'test_widget.dart';
|
||||||
|
|
||||||
@@ -79,8 +75,8 @@ class TestWidget {
|
|||||||
''';
|
''';
|
||||||
|
|
||||||
const expectedOutput = '''
|
const expectedOutput = '''
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// dart format width=80
|
// dart format width=80
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'test_widget.dart';
|
part of 'test_widget.dart';
|
||||||
|
|
||||||
@@ -117,8 +113,8 @@ class TestWidget {
|
|||||||
''';
|
''';
|
||||||
|
|
||||||
const expectedOutput = '''
|
const expectedOutput = '''
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// dart format width=80
|
// dart format width=80
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'test_widget.dart';
|
part of 'test_widget.dart';
|
||||||
|
|
||||||
@@ -128,8 +124,9 @@ part of 'test_widget.dart';
|
|||||||
|
|
||||||
mixin _\$TestWidget {
|
mixin _\$TestWidget {
|
||||||
void _inject(TestWidget instance) {
|
void _inject(TestWidget instance) {
|
||||||
instance.service =
|
instance.service = CherryPick.openRootScope().resolve<MyService>(
|
||||||
CherryPick.openRootScope().resolve<MyService>(named: 'myService');
|
named: 'myService',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
''';
|
''';
|
||||||
@@ -154,8 +151,8 @@ class TestWidget {
|
|||||||
''';
|
''';
|
||||||
|
|
||||||
const expectedOutput = '''
|
const expectedOutput = '''
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// dart format width=80
|
// dart format width=80
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'test_widget.dart';
|
part of 'test_widget.dart';
|
||||||
|
|
||||||
@@ -165,8 +162,9 @@ part of 'test_widget.dart';
|
|||||||
|
|
||||||
mixin _\$TestWidget {
|
mixin _\$TestWidget {
|
||||||
void _inject(TestWidget instance) {
|
void _inject(TestWidget instance) {
|
||||||
instance.service =
|
instance.service = CherryPick.openRootScope().tryResolve<MyService>(
|
||||||
CherryPick.openRootScope().tryResolve<MyService>(named: 'myService');
|
named: 'myService',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
''';
|
''';
|
||||||
@@ -193,8 +191,8 @@ class TestWidget {
|
|||||||
''';
|
''';
|
||||||
|
|
||||||
const expectedOutput = '''
|
const expectedOutput = '''
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// dart format width=80
|
// dart format width=80
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'test_widget.dart';
|
part of 'test_widget.dart';
|
||||||
|
|
||||||
@@ -231,8 +229,8 @@ class TestWidget {
|
|||||||
''';
|
''';
|
||||||
|
|
||||||
const expectedOutput = '''
|
const expectedOutput = '''
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// dart format width=80
|
// dart format width=80
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'test_widget.dart';
|
part of 'test_widget.dart';
|
||||||
|
|
||||||
@@ -242,9 +240,9 @@ part of 'test_widget.dart';
|
|||||||
|
|
||||||
mixin _\$TestWidget {
|
mixin _\$TestWidget {
|
||||||
void _inject(TestWidget instance) {
|
void _inject(TestWidget instance) {
|
||||||
instance.service =
|
instance.service = CherryPick.openScope(
|
||||||
CherryPick.openScope(scopeName: 'userScope')
|
scopeName: 'userScope',
|
||||||
.resolve<MyService>(named: 'myService');
|
).resolve<MyService>(named: 'myService');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
''';
|
''';
|
||||||
@@ -270,8 +268,8 @@ class TestWidget {
|
|||||||
''';
|
''';
|
||||||
|
|
||||||
const expectedOutput = '''
|
const expectedOutput = '''
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// dart format width=80
|
// dart format width=80
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'test_widget.dart';
|
part of 'test_widget.dart';
|
||||||
|
|
||||||
@@ -305,8 +303,8 @@ class TestWidget {
|
|||||||
''';
|
''';
|
||||||
|
|
||||||
const expectedOutput = '''
|
const expectedOutput = '''
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// dart format width=80
|
// dart format width=80
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'test_widget.dart';
|
part of 'test_widget.dart';
|
||||||
|
|
||||||
@@ -341,8 +339,8 @@ class TestWidget {
|
|||||||
''';
|
''';
|
||||||
|
|
||||||
const expectedOutput = '''
|
const expectedOutput = '''
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// dart format width=80
|
// dart format width=80
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'test_widget.dart';
|
part of 'test_widget.dart';
|
||||||
|
|
||||||
@@ -352,8 +350,9 @@ part of 'test_widget.dart';
|
|||||||
|
|
||||||
mixin _\$TestWidget {
|
mixin _\$TestWidget {
|
||||||
void _inject(TestWidget instance) {
|
void _inject(TestWidget instance) {
|
||||||
instance.service =
|
instance.service = CherryPick.openRootScope().resolveAsync<MyService>(
|
||||||
CherryPick.openRootScope().resolveAsync<MyService>(named: 'myService');
|
named: 'myService',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
''';
|
''';
|
||||||
@@ -392,8 +391,8 @@ class TestWidget {
|
|||||||
''';
|
''';
|
||||||
|
|
||||||
const expectedOutput = '''
|
const expectedOutput = '''
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// dart format width=80
|
// dart format width=80
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'test_widget.dart';
|
part of 'test_widget.dart';
|
||||||
|
|
||||||
@@ -404,11 +403,13 @@ part of 'test_widget.dart';
|
|||||||
mixin _\$TestWidget {
|
mixin _\$TestWidget {
|
||||||
void _inject(TestWidget instance) {
|
void _inject(TestWidget instance) {
|
||||||
instance.apiService = CherryPick.openRootScope().resolve<ApiService>();
|
instance.apiService = CherryPick.openRootScope().resolve<ApiService>();
|
||||||
instance.cacheService =
|
instance.cacheService = CherryPick.openRootScope().tryResolve<CacheService>(
|
||||||
CherryPick.openRootScope().tryResolve<CacheService>(named: 'cache');
|
named: 'cache',
|
||||||
|
);
|
||||||
instance.dbService =
|
instance.dbService =
|
||||||
CherryPick.openScope(scopeName: 'dbScope')
|
CherryPick.openScope(
|
||||||
.resolveAsync<DatabaseService>();
|
scopeName: 'dbScope',
|
||||||
|
).resolveAsync<DatabaseService>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
''';
|
''';
|
||||||
@@ -438,8 +439,8 @@ class TestWidget {
|
|||||||
''';
|
''';
|
||||||
|
|
||||||
const expectedOutput = '''
|
const expectedOutput = '''
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// dart format width=80
|
// dart format width=80
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'test_widget.dart';
|
part of 'test_widget.dart';
|
||||||
|
|
||||||
@@ -495,8 +496,8 @@ class TestWidget {
|
|||||||
''';
|
''';
|
||||||
|
|
||||||
const expectedOutput = '''
|
const expectedOutput = '''
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// dart format width=80
|
// dart format width=80
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'test_widget.dart';
|
part of 'test_widget.dart';
|
||||||
|
|
||||||
@@ -532,8 +533,8 @@ class TestWidget {
|
|||||||
''';
|
''';
|
||||||
|
|
||||||
const expectedOutput = '''
|
const expectedOutput = '''
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// dart format width=80
|
// dart format width=80
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'test_widget.dart';
|
part of 'test_widget.dart';
|
||||||
|
|
||||||
@@ -568,8 +569,8 @@ class TestWidget {
|
|||||||
''';
|
''';
|
||||||
|
|
||||||
const expectedOutput = '''
|
const expectedOutput = '''
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// dart format width=80
|
// dart format width=80
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'test_widget.dart';
|
part of 'test_widget.dart';
|
||||||
|
|
||||||
@@ -592,41 +593,10 @@ mixin _\$TestWidget {
|
|||||||
|
|
||||||
/// Helper function to test code generation
|
/// Helper function to test code generation
|
||||||
Future<void> _testGeneration(String input, String expectedOutput) async {
|
Future<void> _testGeneration(String input, String expectedOutput) async {
|
||||||
final readerWriter = TestReaderWriter(rootPackage: 'a');
|
await testBuilder(
|
||||||
await readerWriter.testing.loadIsolateSources();
|
|
||||||
final packageConfig = await loadPackageConfigUri(
|
|
||||||
(await Isolate.packageConfig)!,
|
|
||||||
);
|
|
||||||
final outputs = expectedOutput.isEmpty
|
|
||||||
? null
|
|
||||||
: {
|
|
||||||
'a|lib/test_widget.inject.cherrypick.g.dart':
|
|
||||||
decodedMatches(_normalizedEquals(expectedOutput)),
|
|
||||||
};
|
|
||||||
final result = await testBuilder(
|
|
||||||
injectBuilder(BuilderOptions.empty),
|
injectBuilder(BuilderOptions.empty),
|
||||||
{'a|lib/test_widget.dart': input},
|
{'a|lib/test_widget.dart': input},
|
||||||
outputs: outputs,
|
outputs: {'a|lib/test_widget.inject.cherrypick.g.dart': expectedOutput},
|
||||||
readerWriter: readerWriter,
|
readerWriter: TestReaderWriter(),
|
||||||
rootPackage: 'a',
|
|
||||||
packageConfig: packageConfig,
|
|
||||||
);
|
|
||||||
if (expectedOutput.isEmpty && result.buildResult.status == BuildStatus.failure) {
|
|
||||||
throw InvalidGenerationSourceError('Build failed');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Matcher _normalizedEquals(String expected) {
|
|
||||||
return predicate<String>(
|
|
||||||
(actual) => _normalize(actual) == _normalize(expected),
|
|
||||||
'matches after normalization',
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
String _normalize(String input) {
|
|
||||||
return input
|
|
||||||
.replaceAll(RegExp(r'\s+'), '')
|
|
||||||
.replaceAll(RegExp(r',\)'), ')')
|
|
||||||
.replaceAll(RegExp(r',\]'), ']')
|
|
||||||
.replaceAll(RegExp(r',\}'), '}');
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -11,16 +11,12 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import 'dart:isolate';
|
|
||||||
|
|
||||||
import 'package:build/build.dart';
|
|
||||||
import 'package:build_test/build_test.dart';
|
|
||||||
import 'package:build_runner_core/build_runner_core.dart';
|
|
||||||
import 'package:package_config/package_config.dart';
|
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
|
import 'package:build_test/build_test.dart';
|
||||||
|
import 'package:build/build.dart';
|
||||||
|
|
||||||
import 'package:source_gen/source_gen.dart';
|
|
||||||
import 'package:cherrypick_generator/module_generator.dart';
|
import 'package:cherrypick_generator/module_generator.dart';
|
||||||
|
import 'package:source_gen/source_gen.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group('ModuleGenerator Tests', () {
|
group('ModuleGenerator Tests', () {
|
||||||
@@ -44,8 +40,8 @@ abstract class TestModule extends Module {
|
|||||||
''';
|
''';
|
||||||
|
|
||||||
const expectedOutput = '''
|
const expectedOutput = '''
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// dart format width=80
|
// dart format width=80
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'test_module.dart';
|
part of 'test_module.dart';
|
||||||
|
|
||||||
@@ -79,8 +75,8 @@ abstract class TestModule extends Module {
|
|||||||
''';
|
''';
|
||||||
|
|
||||||
const expectedOutput = '''
|
const expectedOutput = '''
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// dart format width=80
|
// dart format width=80
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'test_module.dart';
|
part of 'test_module.dart';
|
||||||
|
|
||||||
@@ -117,8 +113,8 @@ abstract class TestModule extends Module {
|
|||||||
''';
|
''';
|
||||||
|
|
||||||
const expectedOutput = '''
|
const expectedOutput = '''
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// dart format width=80
|
// dart format width=80
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'test_module.dart';
|
part of 'test_module.dart';
|
||||||
|
|
||||||
@@ -153,8 +149,8 @@ abstract class TestModule extends Module {
|
|||||||
''';
|
''';
|
||||||
|
|
||||||
const expectedOutput = '''
|
const expectedOutput = '''
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// dart format width=80
|
// dart format width=80
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'test_module.dart';
|
part of 'test_module.dart';
|
||||||
|
|
||||||
@@ -191,8 +187,8 @@ abstract class TestModule extends Module {
|
|||||||
''';
|
''';
|
||||||
|
|
||||||
const expectedOutput = '''
|
const expectedOutput = '''
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// dart format width=80
|
// dart format width=80
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'test_module.dart';
|
part of 'test_module.dart';
|
||||||
|
|
||||||
@@ -228,8 +224,8 @@ abstract class TestModule extends Module {
|
|||||||
''';
|
''';
|
||||||
|
|
||||||
const expectedOutput = '''
|
const expectedOutput = '''
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// dart format width=80
|
// dart format width=80
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'test_module.dart';
|
part of 'test_module.dart';
|
||||||
|
|
||||||
@@ -268,8 +264,8 @@ abstract class TestModule extends Module {
|
|||||||
''';
|
''';
|
||||||
|
|
||||||
const expectedOutput = '''
|
const expectedOutput = '''
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// dart format width=80
|
// dart format width=80
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'test_module.dart';
|
part of 'test_module.dart';
|
||||||
|
|
||||||
@@ -303,8 +299,8 @@ abstract class TestModule extends Module {
|
|||||||
''';
|
''';
|
||||||
|
|
||||||
const expectedOutput = '''
|
const expectedOutput = '''
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// dart format width=80
|
// dart format width=80
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'test_module.dart';
|
part of 'test_module.dart';
|
||||||
|
|
||||||
@@ -338,8 +334,8 @@ abstract class TestModule extends Module {
|
|||||||
''';
|
''';
|
||||||
|
|
||||||
const expectedOutput = '''
|
const expectedOutput = '''
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// dart format width=80
|
// dart format width=80
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'test_module.dart';
|
part of 'test_module.dart';
|
||||||
|
|
||||||
@@ -378,8 +374,8 @@ abstract class TestModule extends Module {
|
|||||||
''';
|
''';
|
||||||
|
|
||||||
const expectedOutput = '''
|
const expectedOutput = '''
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// dart format width=80
|
// dart format width=80
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'test_module.dart';
|
part of 'test_module.dart';
|
||||||
|
|
||||||
@@ -418,8 +414,8 @@ abstract class TestModule extends Module {
|
|||||||
''';
|
''';
|
||||||
|
|
||||||
const expectedOutput = '''
|
const expectedOutput = '''
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// dart format width=80
|
// dart format width=80
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'test_module.dart';
|
part of 'test_module.dart';
|
||||||
|
|
||||||
@@ -457,8 +453,8 @@ abstract class TestModule extends Module {
|
|||||||
''';
|
''';
|
||||||
|
|
||||||
const expectedOutput = '''
|
const expectedOutput = '''
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// dart format width=80
|
// dart format width=80
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'test_module.dart';
|
part of 'test_module.dart';
|
||||||
|
|
||||||
@@ -492,8 +488,8 @@ abstract class TestModule extends Module {
|
|||||||
''';
|
''';
|
||||||
|
|
||||||
const expectedOutput = '''
|
const expectedOutput = '''
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// dart format width=80
|
// dart format width=80
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'test_module.dart';
|
part of 'test_module.dart';
|
||||||
|
|
||||||
@@ -545,8 +541,8 @@ abstract class TestModule extends Module {
|
|||||||
''';
|
''';
|
||||||
|
|
||||||
const expectedOutput = '''
|
const expectedOutput = '''
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
|
||||||
// dart format width=80
|
// dart format width=80
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
part of 'test_module.dart';
|
part of 'test_module.dart';
|
||||||
|
|
||||||
@@ -641,41 +637,10 @@ abstract class TestModule extends Module {
|
|||||||
|
|
||||||
/// Helper function to test code generation
|
/// Helper function to test code generation
|
||||||
Future<void> _testGeneration(String input, String expectedOutput) async {
|
Future<void> _testGeneration(String input, String expectedOutput) async {
|
||||||
final readerWriter = TestReaderWriter(rootPackage: 'a');
|
await testBuilder(
|
||||||
await readerWriter.testing.loadIsolateSources();
|
|
||||||
final packageConfig = await loadPackageConfigUri(
|
|
||||||
(await Isolate.packageConfig)!,
|
|
||||||
);
|
|
||||||
final outputs = expectedOutput.isEmpty
|
|
||||||
? null
|
|
||||||
: {
|
|
||||||
'a|lib/test_module.module.cherrypick.g.dart':
|
|
||||||
decodedMatches(_normalizedEquals(expectedOutput)),
|
|
||||||
};
|
|
||||||
final result = await testBuilder(
|
|
||||||
moduleBuilder(BuilderOptions.empty),
|
moduleBuilder(BuilderOptions.empty),
|
||||||
{'a|lib/test_module.dart': input},
|
{'a|lib/test_module.dart': input},
|
||||||
outputs: outputs,
|
outputs: {'a|lib/test_module.module.cherrypick.g.dart': expectedOutput},
|
||||||
readerWriter: readerWriter,
|
readerWriter: TestReaderWriter(),
|
||||||
rootPackage: 'a',
|
|
||||||
packageConfig: packageConfig,
|
|
||||||
);
|
|
||||||
if (expectedOutput.isEmpty && result.buildResult.status == BuildStatus.failure) {
|
|
||||||
throw InvalidGenerationSourceError('Build failed');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Matcher _normalizedEquals(String expected) {
|
|
||||||
return predicate<String>(
|
|
||||||
(actual) => _normalize(actual) == _normalize(expected),
|
|
||||||
'matches after normalization',
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
String _normalize(String input) {
|
|
||||||
return input
|
|
||||||
.replaceAll(RegExp(r'\s+'), '')
|
|
||||||
.replaceAll(RegExp(r',\)'), ')')
|
|
||||||
.replaceAll(RegExp(r',\]'), ']')
|
|
||||||
.replaceAll(RegExp(r',\}'), '}');
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ packages:
|
|||||||
path: "../../cherrypick_annotations"
|
path: "../../cherrypick_annotations"
|
||||||
relative: true
|
relative: true
|
||||||
source: path
|
source: path
|
||||||
version: "4.0.0-dev.0"
|
version: "3.0.2"
|
||||||
cherrypick_flutter:
|
cherrypick_flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -148,7 +148,7 @@ packages:
|
|||||||
path: "../../cherrypick_generator"
|
path: "../../cherrypick_generator"
|
||||||
relative: true
|
relative: true
|
||||||
source: path
|
source: path
|
||||||
version: "4.0.0-dev.0"
|
version: "3.0.2"
|
||||||
clock:
|
clock:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -182,14 +182,14 @@ packages:
|
|||||||
path: "../../cherrypick_annotations"
|
path: "../../cherrypick_annotations"
|
||||||
relative: true
|
relative: true
|
||||||
source: path
|
source: path
|
||||||
version: "4.0.0-dev.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: "4.0.0-dev.0"
|
version: "3.0.2"
|
||||||
cli_launcher:
|
cli_launcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -64,10 +64,14 @@
|
|||||||
- **WHEN** метод помечен одновременно `@instance` и `@provide`
|
- **WHEN** метод помечен одновременно `@instance` и `@provide`
|
||||||
- **THEN** генератор завершает сборку с ошибкой валидации
|
- **THEN** генератор завершает сборку с ошибкой валидации
|
||||||
|
|
||||||
#### Scenario: Требования к @named
|
#### Scenario: Требования к @named на provider-методе
|
||||||
- **WHEN** `@named` использует пустую строку или некорректный идентификатор
|
- **WHEN** `@named` на provider-методе использует пустую строку или некорректный идентификатор
|
||||||
- **THEN** генератор завершает сборку с ошибкой валидации
|
- **THEN** генератор завершает сборку с ошибкой валидации
|
||||||
|
|
||||||
|
#### Scenario: Пустой @named на inject-поле
|
||||||
|
- **WHEN** `@named('')` указан на поле с `@inject`
|
||||||
|
- **THEN** генератор трактует поле как безымянный резолв (без параметра `named`)
|
||||||
|
|
||||||
#### Scenario: Валидность @module
|
#### Scenario: Валидность @module
|
||||||
- **WHEN** класс с `@module` не имеет публичных методов
|
- **WHEN** класс с `@module` не имеет публичных методов
|
||||||
- **THEN** генератор завершает сборку с ошибкой валидации
|
- **THEN** генератор завершает сборку с ошибкой валидации
|
||||||
|
|||||||
@@ -23,11 +23,11 @@
|
|||||||
- **THEN** subscope удаляется из дерева, а связанные ресурсы освобождаются
|
- **THEN** subscope удаляется из дерева, а связанные ресурсы освобождаются
|
||||||
|
|
||||||
#### Scenario: Путь scope и разделитель
|
#### Scenario: Путь scope и разделитель
|
||||||
- **WHEN** scope открывается по иерархическому пути с разделителем
|
- **WHEN** вызывается `CherryPick.openScope(scopeName: ..., separator: ...)` с иерархическим путем
|
||||||
- **THEN** создается цепочка subscopes по каждому сегменту пути
|
- **THEN** создается цепочка subscopes по каждому сегменту пути
|
||||||
|
|
||||||
#### Scenario: Пустой scopeName
|
#### Scenario: Пустой scopeName
|
||||||
- **WHEN** scope открывается с пустым именем
|
- **WHEN** вызывается `CherryPick.openScope(scopeName: '')`
|
||||||
- **THEN** возвращается root scope
|
- **THEN** возвращается root scope
|
||||||
|
|
||||||
### Requirement: Установка и удаление модулей
|
### Requirement: Установка и удаление модулей
|
||||||
@@ -81,8 +81,8 @@
|
|||||||
### Requirement: Ошибки несоответствия sync/async
|
### Requirement: Ошибки несоответствия sync/async
|
||||||
Резолв MUST выбрасывать ошибки при несоответствии sync/async режима.
|
Резолв MUST выбрасывать ошибки при несоответствии sync/async режима.
|
||||||
|
|
||||||
#### Scenario: ResolveSync для async‑инстанса
|
#### Scenario: Синхронный резолв для async‑binding
|
||||||
- **WHEN** binding зарегистрирован как async‑инстанс или async‑provider, а вызывается `resolveSync`
|
- **WHEN** binding зарегистрирован как async‑инстанс или async‑provider, а вызывается `resolve<T>()` или `tryResolve<T>()`
|
||||||
- **THEN** выбрасывается ошибка с указанием использовать async‑резолв
|
- **THEN** выбрасывается ошибка с указанием использовать async‑резолв
|
||||||
|
|
||||||
### Requirement: Управление Disposable
|
### Requirement: Управление Disposable
|
||||||
@@ -111,7 +111,7 @@
|
|||||||
- **THEN** observer получает соответствующие уведомления
|
- **THEN** observer получает соответствующие уведомления
|
||||||
|
|
||||||
### Requirement: Ошибки и сообщения об ошибках
|
### Requirement: Ошибки и сообщения об ошибках
|
||||||
При критических сбоях резолва ядро MUST выбрасывать ошибку с понятным сообщением, а для tryResolve MUST не бросать исключения.
|
При критических сбоях резолва ядро MUST выбрасывать ошибку с понятным сообщением. Для отсутствующей зависимости `tryResolve`/`tryResolveAsync` MUST возвращать `null` без исключения; ошибки выполнения резолва (например, цикл, sync/async mismatch, отсутствие обязательных params) MAY быть проброшены.
|
||||||
|
|
||||||
#### Scenario: Ошибка отсутствующей зависимости
|
#### Scenario: Ошибка отсутствующей зависимости
|
||||||
- **WHEN** вызывается `resolve<T>()` для незарегистрированной зависимости
|
- **WHEN** вызывается `resolve<T>()` для незарегистрированной зависимости
|
||||||
|
|||||||
@@ -41,14 +41,14 @@ Flutter‑интеграция MUST предоставлять `CherryPickProvid
|
|||||||
|
|
||||||
#### Scenario: Ошибка lookup
|
#### Scenario: Ошибка lookup
|
||||||
- **WHEN** вызов происходит вне поддерева провайдера
|
- **WHEN** вызов происходит вне поддерева провайдера
|
||||||
- **THEN** происходит assertion‑ошибка
|
- **THEN** в debug‑режиме происходит assertion‑ошибка
|
||||||
|
|
||||||
### Requirement: Ошибки и сообщения
|
### Requirement: Ошибки и сообщения
|
||||||
При отсутствии провайдера в дереве MUST быть диагностируемая ошибка.
|
При отсутствии провайдера в дереве MUST быть диагностируемая ошибка.
|
||||||
|
|
||||||
#### Scenario: Диагностика отсутствия провайдера
|
#### Scenario: Диагностика отсутствия провайдера
|
||||||
- **WHEN** `CherryPickProvider.of(context)` не находит провайдер
|
- **WHEN** `CherryPickProvider.of(context)` не находит провайдер
|
||||||
- **THEN** сообщение об ошибке указывает на отсутствие провайдера
|
- **THEN** в debug‑режиме сообщение assertion указывает на отсутствие провайдера
|
||||||
|
|
||||||
### Requirement: Точки расширения
|
### Requirement: Точки расширения
|
||||||
Flutter‑интеграция MUST позволять использовать собственные DI‑scope стратегии поверх `CherryPickProvider`.
|
Flutter‑интеграция MUST позволять использовать собственные DI‑scope стратегии поверх `CherryPickProvider`.
|
||||||
|
|||||||
Reference in New Issue
Block a user