build method

  1. @override
Future<void> build(
  1. BuildStep buildStep
)

Generates the outputs for a given BuildStep.

Implementation

@override
Future<void> build(BuildStep buildStep) async {
  final AssetId inputId = buildStep.inputId;
  final AssetId outputId = AssetId(
    inputId.package,
    inputId.path.replaceAll('.dart', '.g.dart'),
  );

  final LibraryElement library = await buildStep.resolver.libraryFor(inputId);
  final StringBuffer generatedCode = StringBuffer();

  // Check if this file has any dependency injection annotations
  final bool hasDependencyInjectionAnnotations =
      library.children.any((Element element) {
    if (element.metadata.annotations.isEmpty) {
      return false;
    }

    for (final ElementAnnotation metadata in element.metadata.annotations) {
      final Element? annotationElement = metadata.element;
      if (annotationElement != null) {
        final String? annotationType = annotationElement.library?.name;
        final String annotationName = annotationElement.displayName;

        if (_isDependencyInjectionAnnotation(
            annotationName, annotationType)) {
          return true;
        }
      }
    }
    return false;
  });

  if (!hasDependencyInjectionAnnotations) {
    return;
  }

  generatedCode.writeln('// GENERATED CODE - DO NOT MODIFY BY HAND');
  generatedCode.writeln('// Generated by di_generator_build');
  generatedCode.writeln(' ');
  generatedCode.writeln("import 'package:get_it/get_it.dart';");
  generatedCode
      .writeln("import 'package:di_generator_build/get_it_extension.dart';");
  generatedCode.writeln("import '${inputId.pathSegments.last}';");

  // Note: Dependencies between generated files should be handled by the consuming project
  // by importing the necessary .g.dart files in their main application code

  generatedCode.writeln(' ');

  final Set<String> processedElements = <String>{};

  for (final Generator generator in _generators) {
    if (generator is GeneratorForAnnotation) {
      final List<Element> elements = library.children
          .where((Element element) => element.metadata.annotations.isNotEmpty)
          .toList();

      for (final Element element in elements) {
        for (final ElementAnnotation metadata in element.metadata.annotations) {
          final Element? annotation = metadata.element;
          if (annotation != null) {
            final String key = '${element.name}_${annotation.name}';

            if (!processedElements.contains(key)) {
              try {
                final ConstantReader constantReader =
                    ConstantReader(metadata.computeConstantValue());
                final String generated =
                    generator.generateForAnnotatedElement(
                  element,
                  constantReader,
                  buildStep,
                );
                if (generated.isNotEmpty) {
                  generatedCode.writeln(generated);
                  processedElements.add(key);
                }
              } on Exception {
                // Skip if annotation doesn't match
              }
            }
          }
        }
      }
    }
  }

  if (generatedCode.isNotEmpty) {
    // Write to build cache
    await buildStep.writeAsString(outputId, generatedCode.toString());

    // Try to write to source directory for better developer experience
    try {
      final String packageName = buildStep.inputId.package;
      if (packageName != 'di_generator_build') {
        final String sourcePath = inputId.path.replaceAll('.dart', '.g.dart');
        final File sourceFile = File(sourcePath);

        // Ensure the directory exists
        final Directory dir = sourceFile.parent;
        if (!dir.existsSync()) {
          dir.createSync(recursive: true);
        }

        await sourceFile.writeAsString(generatedCode.toString());
      }
    } on Exception {
      // If we can't write to source directory, that's okay
      // The build cache version will still work
    }
  }
}