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 {
  if (!await buildStep.resolver.isLibrary(buildStep.inputId)) {
    await buildStep.writeAsString(
      buildStep.inputId.changeExtension(_extension),
      'external void initReflector();',
    );
    return;
  }
  final library = await buildStep.inputLibrary;
  final components = <ClassElement>[];
  final directives = <ClassElement>[];
  final injectors = <String>[];
  var units = [library.definingCompilationUnit, ...library.parts];
  var types = units.expand((unit) => unit.types);
  var fields = units.expand((unit) => unit.topLevelVariables);
  for (final clazz in types) {
    final component = $Component.firstAnnotationOfExact(
      clazz,
      throwOnUnresolved: false,
    );
    if (component != null) {
      components.add(clazz);
    } else {
      final directive = $Directive.firstAnnotationOfExact(
        clazz,
        throwOnUnresolved: false,
      );
      if (directive != null) {
        directives.add(clazz);
      }
    }
  }
  for (final field in fields) {
    if ($GenerateInjector.hasAnnotationOfExact(
      field,
      throwOnUnresolved: false,
    )) {
      injectors.add('${field.name}\$Injector');
    }
  }
  // Unlike the main compiler, we do not do an allow-list check here; this is
  // both to speed up the outliner (reducing duplicate checks) and because we
  // do not have a configured CompileContext when the outliner is run.
  final emitNullSafeCode = library.isNonNullableByDefault;
  final languageVersion = emitNullSafeCode ? '' : '// @dart=2.9\n\n';
  final output = StringBuffer('$languageVersion$_analyzerIgnores\n');
  if (exportUserCodeFromTemplate) {
    output
      ..writeln('// The .template.dart files also export the user code.')
      ..writeln("export '${p.basename(buildStep.inputId.path)}';")
      ..writeln();
  }
  if (components.isNotEmpty ||
      directives.isNotEmpty ||
      injectors.isNotEmpty) {
    output
      ..writeln('// Required for referencing runtime code.')
      ..writeln(_angularImports);
    final userLandCode = p.basename(buildStep.inputId.path);
    output
      ..writeln('// Required for specifically referencing user code.')
      ..writeln("import '$userLandCode';")
      ..writeln();
  }

  output.writeln('// Required for "type inference" (scoping).');
  for (final d in library.imports) {
    if (!d.isDeferred && d.uri != null) {
      var directive = "import '${d.uri}'";
      if (d.prefix != null) {
        directive += ' as ${d.prefix!.name}';
      }
      if (d.combinators.isNotEmpty) {
        final isShow = d.combinators.first is ShowElementCombinator;
        directive += isShow ? ' show ' : ' hide ';
        directive += d.combinators
            .map((c) {
              if (c is ShowElementCombinator) {
                return c.shownNames;
              }
              if (c is HideElementCombinator) {
                return c.hiddenNames;
              }
              return const <Object>[];
            })
            .expand((i) => i)
            .join(', ');
      }
      output.writeln('$directive;');
    }
  }
  output.writeln();
  final directiveTypeParameters = await collectTypeParameters(
      components.followedBy(directives), buildStep);
  if (components.isNotEmpty) {
    for (final component in components) {
      final componentName = component.name;
      final typeArguments = _typeArgumentsFor(component);
      final typeParameters = directiveTypeParameters[component.name];
      final componentType = '$componentName$typeArguments';
      final viewName = 'View${componentName}0';
      output.write('''
// For @Component class $componentName.
external List<dynamic> get styles\$$componentName;
external _ng.ComponentFactory<$componentName> get ${componentName}NgFactory;
external _ng.ComponentFactory<$componentType> create${componentName}Factory$typeParameters();
class $viewName$typeParameters extends _ng.ComponentView<$componentType> {
external $viewName(_ng.View parentView, int parentIndex);
external String get debugComponentTypeName;
}
''');
    }
  }
  if (directives.isNotEmpty) {
    for (final directive in directives) {
      final directiveName = directive.name;
      final changeDetectorName = '${directiveName}NgCd';
      final typeArguments = _typeArgumentsFor(directive);
      final typeParameters = directiveTypeParameters[directive.name];
      final directiveType = '$directiveName$typeArguments';
      output.write('''
// For @Directive class $directiveName.
class $changeDetectorName$typeParameters extends _ng.DirectiveChangeDetector {
external $directiveType get instance;
external void deliverChanges();
external $changeDetectorName($directiveType instance);
external void detectHostChanges(_ng.RenderView view, _html.Element hostElement);
}
''');
    }
  }
  if (injectors.isNotEmpty) {
    for (final injector in injectors) {
      output.writeln('external _ng.Injector $injector(_ng.Injector parent);');
    }
  }
  output
    ..writeln()
    ..writeln('external void initReflector();');
  await buildStep.writeAsString(
    buildStep.inputId.changeExtension(_extension),
    output.toString(),
  );
}