Skip to content

Commit

Permalink
Feat: add @ignoreParam annotation to ignore optional parameters in fa…
Browse files Browse the repository at this point in the history
…ctory methods

Refactor: Add annotation target metas
  • Loading branch information
Milad-Akarie committed Mar 13, 2024
1 parent 186a6d6 commit b6f6cb4
Show file tree
Hide file tree
Showing 12 changed files with 126 additions and 34 deletions.
2 changes: 2 additions & 0 deletions injectable/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# ChangeLog
## [2.4.0]
Feat: add @ignoreParam annotation to ignore optional parameters in factory methods
## [2.3.5]
Fix: add generic type annotation to registerSingleton() call #436
## [2.3.4]
Expand Down
2 changes: 1 addition & 1 deletion injectable/lib/src/environment_filter.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

typedef EnvironmentFilterFunc = bool Function(Set<String>);

/// a simple filter function to be used inside [SimpleEnvironmentFilter]
/// filter for whether to register for the given set of environments
Expand Down
46 changes: 36 additions & 10 deletions injectable/lib/src/injectable_annotations.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
/// Marks a top-level function as an initializer function
import 'package:meta/meta_meta.dart' show Target, TargetKind;

/// // Marks a top-level function as an initializer function
/// for configuring Get_it
@Target({TargetKind.function})
class InjectableInit {
/// Only files exist in provided directories will be processed
final List<String> generateForDir;
Expand Down Expand Up @@ -56,12 +59,6 @@ class InjectableInit {
/// defaults to false
final bool throwOnMissingDependencies;

/// a List of external package modules to be registered
/// in the default package initializer
/// classes passed here must extend [MicroPackageModule]
@Deprecated('use externalPackageModulesBefore instead')
final List<Type>? externalPackageModules;

/// a List of external package modules to be registered
/// in the default package initializer before root dependencies
/// classes passed here must extend [MicroPackageModule]
Expand Down Expand Up @@ -91,7 +88,6 @@ class InjectableInit {
this.ignoreUnregisteredTypesInPackages = const [],
this.asExtension = true,
this.usesNullSafety = true,
this.externalPackageModules,
this.throwOnMissingDependencies = false,
this.includeMicroPackages = true,
this.externalPackageModulesAfter,
Expand All @@ -104,7 +100,6 @@ class InjectableInit {
this.generateForDir = const ['lib'],
this.preferRelativeImports = false,
this.ignoreUnregisteredTypes = const [],
this.externalPackageModules,
this.externalPackageModulesAfter,
this.externalPackageModulesBefore,
this.usesConstructorCallback = false,
Expand All @@ -128,6 +123,8 @@ const microPackageInit = InjectableInit.microPackage();

/// Marks a class as an injectable
/// dependency and generates
@Target({TargetKind.classType, TargetKind.method, TargetKind.getter})
class Injectable {
/// The type to bind your implementation to,
/// typically, an abstract class which is implemented by the
Expand Down Expand Up @@ -156,6 +153,7 @@ const injectable = Injectable();

/// Classes annotated with @Singleton
/// will generate registerSingleton function
@Target({TargetKind.classType, TargetKind.method, TargetKind.getter})
class Singleton extends Injectable {
/// passed to singlesReady property
/// in registerSingleton function
Expand Down Expand Up @@ -188,6 +186,7 @@ const singleton = Singleton();

/// Classes annotated with @LazySingleton
/// will generate registerLazySingleton func
@Target({TargetKind.classType, TargetKind.method, TargetKind.getter})
class LazySingleton extends Injectable {
/// default constructor
const LazySingleton({
Expand All @@ -211,6 +210,12 @@ const lazySingleton = LazySingleton();
/// Used to register a dependency under a name
/// instead of type also used to annotated
/// named injected dependencies in constructors
@Target({
TargetKind.classType,
TargetKind.parameter,
TargetKind.method,
TargetKind.getter
})
class Named {
/// The name in which an instance is registered
final String? name;
Expand All @@ -233,6 +238,7 @@ const named = Named('');

/// Used to annotate dependencies which are
/// registered under certain environments
@Target({TargetKind.classType, TargetKind.method, TargetKind.getter})
class Environment {
/// name of the environment
final String name;
Expand Down Expand Up @@ -267,11 +273,12 @@ const test = Environment(Environment.test);

/// Marks a factory, a named constructor or a static create
/// function as an injectable constructor
/// if not added the default constructor will be used.
/// if not added the default constructor will be used
class FactoryMethod {
/// return value will be pre-awaited before it's
/// registered inside of GetIt
final bool preResolve;

// default constructor
const FactoryMethod({this.preResolve = false});
}
Expand All @@ -283,6 +290,7 @@ const factoryMethod = FactoryMethod();
/// Marks a constructor param as
/// factoryParam so it can be passed
/// to the resolver function
@Target({TargetKind.parameter})
class FactoryParam {
const FactoryParam._();
}
Expand All @@ -291,9 +299,21 @@ class FactoryParam {
/// with default arguments
const factoryParam = FactoryParam._();

/// Constructor params annotated with [IgnoreParam]
/// will be ignored by when generating the
/// resolver function
@Target({TargetKind.parameter})
class IgnoreParam {
const IgnoreParam._();
}

/// const instance of [IgnoreParam]
const ignoreParam = IgnoreParam._();

/// marks a class as a register module where all
/// property accessors rerun types are considered factories
/// unless annotated with @singleton/lazySingleton.
@Target({TargetKind.classType})
class Module {
const Module._();
}
Expand All @@ -305,6 +325,7 @@ const module = Module._();
/// Futures annotated with [preResolve]
/// will be pre-awaited before they're
/// registered inside of GetIt
@Target({TargetKind.method})
class PreResolve {
const PreResolve._();
}
Expand All @@ -316,10 +337,12 @@ const preResolve = PreResolve._();
/// methods annotated with [postConstruct]
/// will be called in a cascade manner
/// after being constructed
@Target({TargetKind.method})
class PostConstruct {
/// return value will be pre-awaited before it's
/// registered inside of GetIt
final bool preResolve;

// default constructor
const PostConstruct({this.preResolve = false});
}
Expand All @@ -330,6 +353,7 @@ const postConstruct = PostConstruct();

/// marks an instance method as a dispose
/// call back to be passed to [GetIt]
@Target({TargetKind.method})
class DisposeMethod {
const DisposeMethod._();
}
Expand All @@ -340,6 +364,7 @@ const disposeMethod = DisposeMethod._();

/// Classes annotated with @Order will overwrite
/// the automatically generated position of the
@Target({TargetKind.classType})
class Order {
/// determines the position in the order of generated GetIt functions
final int position;
Expand All @@ -354,6 +379,7 @@ const order = Order(0);

/// Used to annotate dependencies which are
/// registered under a different scope than main-scope
@Target({TargetKind.classType})
class Scope {
/// name of the scope
final String name;
Expand Down
2 changes: 1 addition & 1 deletion injectable/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ packages:
source: hosted
version: "3.0.0"
meta:
dependency: transitive
dependency: "direct main"
description:
name: meta
sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
Expand Down
3 changes: 2 additions & 1 deletion injectable/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
name: injectable
description: Injectable is a convenient code generator for get_it. Inspired by Angular DI, Guice DI and inject.dart.
version: 2.3.5
version: 2.4.0
homepage: https://github.com/Milad-Akarie/injectable

environment:
sdk: ">=3.0.0 <4.0.0"

dependencies:
get_it: ">=7.2.0 <8.0.0"
meta: ^1.12.0

dev_dependencies:
lints: ^3.0.0
2 changes: 2 additions & 0 deletions injectable_generator/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# ChangeLog
## [2.6.0]
Feat: add @ignoreParam annotation to ignore optional parameters in factory methods
## [2.5.1]
Fix: Type Alias error on generation #437
## [2.5.0]
Expand Down
11 changes: 11 additions & 0 deletions injectable_generator/lib/resolvers/dependency_resolver.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import '../injectable_types.dart';
import 'importable_type_resolver.dart';

const TypeChecker _namedChecker = TypeChecker.fromRuntime(Named);
const TypeChecker _ignoredChecker = TypeChecker.fromRuntime(IgnoreParam);
const TypeChecker _injectableChecker = TypeChecker.fromRuntime(Injectable);
const TypeChecker _envChecker = TypeChecker.fromRuntime(Environment);
const TypeChecker _preResolveChecker = TypeChecker.fromRuntime(PreResolve);
Expand Down Expand Up @@ -254,6 +255,16 @@ class DependencyResolver {
_isAsync = executableInitializer.returnType.isDartAsyncFuture;
_constructorName = executableInitializer.name;
for (ParameterElement param in executableInitializer.parameters) {
final ignoredAnnotation = _ignoredChecker.firstAnnotationOf(param);

if (ignoredAnnotation != null) {
throwIf(
!param.isOptional,
'Params annotated with @ignoreParam must be optional',
element: param,
);
continue;
}
final namedAnnotation = _namedChecker.firstAnnotationOf(param);
final instanceName = namedAnnotation
?.getField('type')
Expand Down
37 changes: 26 additions & 11 deletions injectable_generator/lib/resolvers/importable_type_resolver.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,25 @@ abstract class ImportableTypeResolver {

ImportableType resolveType(DartType type);

ImportableType resolveFunctionType(FunctionType function, [ExecutableElement? executableElement]);
ImportableType resolveFunctionType(FunctionType function,
[ExecutableElement? executableElement]);

static String? relative(String? path, Uri? to) {
if (path == null || to == null) {
return null;
}
var fileUri = Uri.parse(path);
var libName = to.pathSegments.first;
if ((to.scheme == 'package' && fileUri.scheme == 'package' && fileUri.pathSegments.first == libName) ||
if ((to.scheme == 'package' &&
fileUri.scheme == 'package' &&
fileUri.pathSegments.first == libName) ||
(to.scheme == 'asset' && fileUri.scheme != 'package')) {
if (fileUri.path == to.path) {
return fileUri.pathSegments.last;
} else {
return p.posix.relative(fileUri.path, from: to.path).replaceFirst('../', '');
return p.posix
.relative(fileUri.path, from: to.path)
.replaceFirst('../', '');
}
} else {
return path;
Expand Down Expand Up @@ -56,7 +61,8 @@ class ImportableTypeResolverImpl extends ImportableTypeResolver {
}
libs.where((e) => e.exportNamespace.definedNames.values.contains(element));
for (var lib in libs) {
if (!_isCoreDartType(lib) && lib.exportNamespace.definedNames.values.contains(element)) {
if (!_isCoreDartType(lib) &&
lib.exportNamespace.definedNames.values.contains(element)) {
imports.add(lib.identifier);
}
}
Expand All @@ -68,8 +74,10 @@ class ImportableTypeResolverImpl extends ImportableTypeResolver {
}

@override
ImportableType resolveFunctionType(FunctionType function, [ExecutableElement? executableElement]) {
final functionElement = executableElement ?? function.element ?? function.alias?.element;
ImportableType resolveFunctionType(FunctionType function,
[ExecutableElement? executableElement]) {
final functionElement =
executableElement ?? function.element ?? function.alias?.element;
if (functionElement == null) {
throw 'Can not resolve function type \nTry using an alias e.g typedef MyFunction = ${function.getDisplayString(withNullability: false)};';
}
Expand All @@ -95,15 +103,20 @@ class ImportableTypeResolverImpl extends ImportableTypeResolver {
List<ImportableType> _resolveTypeArguments(DartType typeToCheck) {
final importableTypes = <ImportableType>[];
if (typeToCheck is RecordType && typeToCheck.alias == null) {
for (final recordField in [...typeToCheck.positionalFields, ...typeToCheck.namedFields]) {
for (final recordField in [
...typeToCheck.positionalFields,
...typeToCheck.namedFields
]) {
final imports = resolveImports(recordField.type.element);
importableTypes.add(ImportableType(
name: recordField.type.element?.name ?? 'void',
import: imports.firstOrNull,
otherImports: imports.skip(1).toSet(),
isNullable: recordField.type.nullabilitySuffix == NullabilitySuffix.question,
isNullable:
recordField.type.nullabilitySuffix == NullabilitySuffix.question,
typeArguments: _resolveTypeArguments(recordField.type),
nameInRecord: recordField is RecordTypeNamedField ? recordField.name : null,
nameInRecord:
recordField is RecordTypeNamedField ? recordField.name : null,
));
}
} else if (typeToCheck is ParameterizedType || typeToCheck.alias != null) {
Expand All @@ -127,7 +140,8 @@ class ImportableTypeResolverImpl extends ImportableTypeResolver {
importableTypes.add(ImportableType(name: 'dynamic'));
} else {
importableTypes.add(ImportableType(
name: type.element?.name ?? type.getDisplayString(withNullability: false),
name: type.element?.name ??
type.getDisplayString(withNullability: false),
import: imports.firstOrNull,
otherImports: imports.skip(1).toSet(),
isNullable: type.nullabilitySuffix == NullabilitySuffix.question,
Expand All @@ -153,7 +167,8 @@ class ImportableTypeResolverImpl extends ImportableTypeResolver {
);
}
return ImportableType(
name: effectiveElement?.displayName ?? type.getDisplayString(withNullability: false),
name: effectiveElement?.displayName ??
type.getDisplayString(withNullability: false),
isNullable: type.nullabilitySuffix == NullabilitySuffix.question,
import: imports.firstOrNull,
otherImports: imports.skip(1).toSet(),
Expand Down
7 changes: 3 additions & 4 deletions injectable_generator/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -252,10 +252,9 @@ packages:
injectable:
dependency: "direct main"
description:
name: injectable
sha256: "44b4aa254816b23bf0a3285ff27da3ff0b516a6f00e0bbbea7b194f14fbb75a7"
url: "https://pub.dev"
source: hosted
path: "../injectable"
relative: true
source: path
version: "2.3.5"
io:
dependency: transitive
Expand Down
4 changes: 2 additions & 2 deletions injectable_generator/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: injectable_generator
description: Injectable is a convenient code generator for get_it. Inspired by Angular DI, Guice DI and inject.dart.
version: 2.5.1
version: 2.6.0
homepage: https://github.com/Milad-Akarie/injectable

environment:
Expand All @@ -15,7 +15,7 @@ dependencies:
code_builder: ^4.10.0
dart_style: ^2.0.3
injectable:
^2.3.5
^2.4.0
# path: ../injectable
collection: ^1.17.1
recase: ^4.1.0
Expand Down
Loading

0 comments on commit b6f6cb4

Please sign in to comment.