build method

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

Generates the outputs for a given BuildStep.

Implementation

@override
Future<void> build(BuildStep step) async {
  assert(step.inputId.path == r'$lib$');

  List<String> outLines = <String>[];
  List<String> imports = <String>[];
  List<ClassElement> allClasses = [];
  List<ClassElement> foundParentOf = [];
  Map<ClassElement, List<ClassElement>> childrenToParents = {};
  Map<ClassElement, List<_ChildModelInfo>> childInfos = {};

  await for (AssetId asset in step.findAssets($dartFilesInLib)) {
    if (!await step.resolver.isLibrary(asset)) continue;
    LibraryElement lib = await step.resolver.libraryFor(asset);
    Iterable<ClassElement> classes = LibraryReader(lib).classes;

    for (ClassElement cls in classes) {
      if (!_hasModelCrudMixin(cls)) continue;

      PropertyAccessorElement? childGetter = cls.lookUpGetter(
        name: 'childModels',
        library: cls.library,
      );

      if (childGetter == null) continue;

      allClasses.add(cls);

      AstNode? getterNode = await step.resolver.astNodeFor(
        childGetter.firstFragment,
        resolve: true,
      );
      if (getterNode is! MethodDeclaration) continue;

      FunctionBody body = getterNode.body;
      Expression? expr = switch (body) {
        ExpressionFunctionBody(:final expression) => expression,
        BlockFunctionBody(:final block) =>
          block.statements.whereType<ReturnStatement>().first.expression,
        _ => null,
      };
      if (expr is! ListLiteral) continue;
      List<_ChildModelInfo> infos = _readFireModels(expr, cls.library);
      childInfos[cls] = infos;
      foundParentOf.addAll(infos.map((i) => i.element));
      for (_ChildModelInfo info in infos) {
        childrenToParents
            .putIfAbsent(info.element, () => <ClassElement>[])
            .add(cls);
      }
    }
  }

  List<Future<(String, String)>> work = [];

  await for (AssetId asset in step.findAssets($dartFilesInLib)) {
    if (!await step.resolver.isLibrary(asset)) continue;
    LibraryElement lib = await step.resolver.libraryFor(asset);
    Iterable<ClassElement> classes = LibraryReader(lib).classes;

    for (ClassElement cls in classes) {
      if (!_hasModelCrudMixin(cls)) continue;
      if (!childInfos.containsKey(cls)) continue;

      List<_ChildModelInfo> infos = childInfos[cls] ?? [];
      imports.add("import '${cls.library.uri}';");

      if ($artifactChecker.hasAnnotationOf(cls, throwOnUnresolved: false)) {
        imports.add(
          "import '${cls.library.uri.replace(path: "${cls.library.uri.pathSegments[0]}/gen/artifacts.gen.dart")}';",
        );
      }

      work.add(_genCrudExtensions(
        cls,
        infos,
        childrenToParents[cls] ?? [],
        childrenToParents,
      ));
    }
  }

  for ((String, String) o in await Future.wait(work)) {
    imports.add(o.$1);
    outLines.add(o.$2);
  }

  List<ClassElement> roots =
      allClasses.where((c) => !foundParentOf.contains(c)).toList();
  for (ClassElement i in roots) {
    (String, String) o = _genRootCrudExtensions(i);
    imports.add(o.$1);
    outLines.add(o.$2);
  }

  AssetId out = AssetId(step.inputId.package, 'lib/gen/crud.gen.dart');
  await step.writeAsString(
    out,
    '// GENERATED – do not modify.\n${(imports.join("\n").split("\n").map((i) => i.replaceAll("'", '"').trim()).toSet()..removeAll(["import '';", 'import "";'])).join("\n")}\n' +
        outLines.join('\n'),
  );
}