generateForAnnotatedElement method

  1. @override
dynamic generateForAnnotatedElement(
  1. Element element,
  2. ConstantReader annotation,
  3. 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.

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 usesNullSafety = annotation.read('usesNullSafety').boolValue;
  final isMicroPackage = annotation.read('_isMicroPackage').boolValue;
  final usesConstructorCallback =
      annotation.read('usesConstructorCallback').boolValue;
  final throwOnMissingDependencies =
      annotation.read('throwOnMissingDependencies').boolValue;
  final targetFile = element.source?.uri;
  final preferRelativeImports =
      annotation.read("preferRelativeImports").boolValue;

  final includeMicroPackages =
      annotation.read("includeMicroPackages").boolValue;

  final rootDir = annotation.peek('rootDir')?.stringValue;

  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);
  if (!isMicroPackage && includeMicroPackages) {
    final glob = Glob('**.module.dart', recursive: true);
    final filesStream = glob.list(root: rootDir);

    await for (final match in filesStream) {
      final commentAnnotation = File(match.path).readAsLinesSync().first;
      if (commentAnnotation.contains('@GeneratedMicroModule')) {
        final segments = commentAnnotation.split(';');
        if (segments.length == 3) {
          final externalModule = ExternalModuleConfig(
            ImportableType(
              name: segments[1],
              import: segments[2],
            ),
          );
          if (!microPackagesModules
              .any((e) => externalModule.module == e.module)) {
            microPackageModulesBefore.add(externalModule);
          }
        }
      }
    }
  }

  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,
  );
  _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 generator = LibraryGenerator(
    dependencies: List.of(deps),
    targetFile: preferRelativeImports ? targetFile : null,
    initializerName: initializerName,
    asExtension: asExtension,
    microPackageName:
        isMicroPackage ? buildStep.inputId.package.pascalCase : null,
    microPackagesModulesBefore: microPackageModulesBefore,
    microPackagesModulesAfter: microPackageModulesAfter,
    usesConstructorCallback: usesConstructorCallback,
  );

  final generatedLib = generator.generate();
  final emitter = DartEmitter(
    allocator: Allocator.simplePrefixing(),
    orderDirectives: true,
    useNullSafetySyntax: usesNullSafety,
  );

  final output =
      DartFormatter().format(generatedLib.accept(emitter).toString());

  if (isMicroPackage) {
    final outputId = buildStep.inputId.changeExtension('.module.dart');
    return buildStep.writeAsString(
      outputId,
      [
        '//@GeneratedMicroModule;${capitalize(buildStep.inputId.package.pascalCase)}PackageModule;${outputId.uri}',
        defaultFileHeader,
        output,
      ].join('\n'),
    );
  }
  return output;
}