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)) return;

  final lib = await buildStep.inputLibrary;
  final reader = LibraryReader(lib);

  final pages = reader.annotatedWith(_pageChecker);
  if (pages.isEmpty) return;

  for (final page in pages) {
    final element = page.element;
    if (element is! ClassElement) continue;

    // Use getGetter to check if the class declares the getter
    final componentsGetter = element.getGetter('components');

    // Ensure it's not inherited from SparkPage (actually getGetter returns declared one primarily,
    // but checking enclosingName is safe)
    final hasComponents =
        componentsGetter != null &&
        componentsGetter.enclosingElement.name != 'SparkPage';

    if (!hasComponents) continue;

    final session = element.session;
    if (session == null) continue;

    final parsedLib = await session.getResolvedLibraryByElement(
      element.library,
    );
    if (parsedLib is! ResolvedLibraryResult) continue;

    MethodDeclaration? getterNode;
    // Search all units for the getter declaration (avoiding Element.source usage)
    for (final unitResult in parsedLib.units) {
      for (final decl in unitResult.unit.declarations) {
        if (decl is ClassDeclaration &&
            decl.namePart.typeName.lexeme ==
                componentsGetter.enclosingElement.name) {
          for (final member in (decl.body as BlockClassBody).members) {
            if (member is MethodDeclaration &&
                member.isGetter &&
                member.name.lexeme == 'components') {
              getterNode = member;
              break;
            }
          }
        }
        if (getterNode != null) break;
      }
      if (getterNode != null) break;
    }

    if (getterNode == null) continue;

    final body = getterNode.body;
    if (body is! ExpressionFunctionBody) continue;

    final list = body.expression;
    if (list is! ListLiteral) continue;

    if (list.elements.isEmpty) continue;

    final componentImports = <String>{};
    final components = <MapEntry<String, String>>[];

    for (final item in list.elements) {
      final referencedClasses = _findReferencedClasses(item);

      for (final componentElement in referencedClasses) {
        final tagField = componentElement.getField('tag');
        if (tagField == null || !tagField.isStatic) continue;

        final constant = tagField.computeConstantValue();
        if (constant == null) continue;
        final tagValue = constant.toStringValue();
        if (tagValue == null) continue;

        // Use library identifier as import URI (definingCompilationUnit is missing)
        final importUri = componentElement.library.identifier;

        componentImports.add(importUri);
        final name = componentElement.name;
        if (name == null) continue;
        components.add(MapEntry(tagValue, name));
      }
    }

    if (components.isEmpty) continue;

    final outputId = buildStep.allowedOutputs.single;

    final content = _generateWebEntry(componentImports, components);
    await buildStep.writeAsString(
      outputId,
      DartFormatter(
        languageVersion: DartFormatter.latestLanguageVersion,
      ).format(content),
    );
  }
}