generateForAnnotatedElement method
- Element element,
- ConstantReader annotation,
- BuildStep buildStep
Implement to return source code to generate for element.
This method is invoked based on finding elements annotated with an
instance of T. The annotation is provided as a ConstantReader.
Supported return values include a single String or multiple String instances within an Iterable or Stream. It is also valid to return a Future of String, Iterable, or Stream. When multiple values are returned through an iterable or stream they will be deduplicated. Typically each value will be an independent unit of code and the deduplication prevents re-defining the same member multiple times. For example if multiple annotated elements may need a specific utility method available it can be output for each one, and the single deduplicated definition can be shared.
Implementations should return null when no content is generated. Empty
or whitespace-only String instances are also ignored.
Implementation
@override
dynamic generateForAnnotatedElement(
Element element,
ConstantReader annotation,
BuildStep buildStep,
) async {
final generateForDir = annotation
.read('generateForDir')
.listValue
.map((e) => e.toStringValue());
final isMicroPackage = annotation.read('_isMicroPackage').boolValue;
final usesConstructorCallback = annotation
.read('usesConstructorCallback')
.boolValue;
final throwOnMissingDependencies = annotation
.read('throwOnMissingDependencies')
.boolValue;
final targetFile = element.firstFragment.libraryFragment?.source.uri;
final preferRelativeImports = annotation
.read("preferRelativeImports")
.boolValue;
final generateForEnvironments = annotation
.read('generateForEnvironments')
.setValue
.map((e) => e.getField('name')?.toStringValue());
final generateAccessors = annotation.read("generateAccessors").boolValue;
final allowMultipleRegistrations = annotation
.read('allowMultipleRegistrations')
.boolValue;
final dirPattern = generateForDir.length > 1
? '{${generateForDir.join(',')}}'
: '${generateForDir.first}';
final injectableConfigFiles = Glob("$dirPattern/**.injectable.json");
final jsonData = <Map>[];
await for (final id in buildStep.findAssets(injectableConfigFiles)) {
final json = jsonDecode(await buildStep.readAsString(id));
jsonData.addAll([...json]);
}
final deps = <DependencyConfig>[];
for (final json in jsonData) {
deps.add(DependencyConfig.fromJson(json));
}
final initializerName = annotation.read('initializerName').stringValue;
final asExtension = annotation.read('asExtension').boolValue;
final typeResolver = ImportableTypeResolverImpl(
await buildStep.resolver.libraries.toList(),
);
final ignoredTypes = annotation
.read('ignoreUnregisteredTypes')
.listValue
.map((e) => typeResolver.resolveType(e.toTypeValue()!));
final microPackageModulesBefore = _getMicroPackageModules(
annotation.peek('externalPackageModulesBefore'),
typeResolver,
);
// remove after deprecation period
if (microPackageModulesBefore.isEmpty) {
microPackageModulesBefore.addAll(
_getMicroPackageModulesMapped(
annotation.peek('externalPackageModules'),
typeResolver,
),
);
}
final microPackageModulesAfter = _getMicroPackageModules(
annotation.peek('externalPackageModulesAfter'),
typeResolver,
);
final microPackagesModules = microPackageModulesBefore.union(
microPackageModulesAfter,
);
final ignoreTypesInPackages = annotation
.read('ignoreUnregisteredTypesInPackages')
.listValue
.map((e) => e.toStringValue())
.where((e) => e != null)
.cast<String>()
.toList();
// we want to ignore unregistered types in microPackages
// because the micro module should handle them
for (final pckModule in microPackagesModules) {
final packageName = Uri.parse(
pckModule.module.import!,
).pathSegments.first;
ignoreTypesInPackages.add(packageName);
}
reportMissingDependencies(
deps,
ignoredTypes,
ignoreTypesInPackages,
targetFile,
throwOnMissingDependencies,
);
if (!allowMultipleRegistrations) {
validateDuplicateDependencies(deps);
}
/// don't allow registering of the same dependency with both async and sync factories
final groupedByType = deps.groupListsBy((d) => (d.type, d.instanceName));
for (final entry in groupedByType.entries) {
final first = entry.value.first;
final isAsync = first.isAsync && !first.preResolve;
throwIf(
entry.value.any((e) => (e.isAsync && !e.preResolve) != isAsync),
'Dependencies of type [${entry.key.$1}] must either all be async or all be sync\n',
);
}
final filteredDeps = generateForEnvironments.isEmpty
? deps
: deps
.where(
(element) =>
element.environments.isEmpty ||
element.environments.any(
(e) => generateForEnvironments.contains(e),
),
)
.toList();
final generator = LibraryGenerator(
dependencies: List.of(filteredDeps),
generateAccessors: generateAccessors,
targetFile: preferRelativeImports ? targetFile : null,
initializerName: initializerName,
asExtension: asExtension,
microPackageName: isMicroPackage
? buildStep.inputId.package.pascalCase
: null,
microPackagesModulesBefore: microPackageModulesBefore,
microPackagesModulesAfter: microPackageModulesAfter,
usesConstructorCallback: usesConstructorCallback,
allowMultipleRegistrations: allowMultipleRegistrations,
);
final generatedLib = generator.generate();
final emitter = DartEmitter(
allocator: HashedAllocator(),
orderDirectives: true,
useNullSafetySyntax: true,
);
final output = DartFormatter(
languageVersion: DartFormatter.latestShortStyleLanguageVersion,
).format(generatedLib.accept(emitter).toString());
if (isMicroPackage) {
final outputId = buildStep.inputId.changeExtension('.module.dart');
return buildStep.writeAsString(
outputId,
[defaultFileHeader, output].join('\n'),
);
}
return output;
}