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 {
  try {
    Map<String, dynamic> pubspec = Map<String, dynamic>.from(
      loadYaml(File("pubspec.yaml").readAsStringSync()),
    );
    artifactConfig =
        Map<String, dynamic>.from(pubspec["artifact"] ?? {}) ?? {};
    artifactConfig["name"] = pubspec["name"] ?? "unknown_package";
    verbose("Loaded Config ${artifactConfig}");
  } catch (e) {
    error(e);
    warn(
      "Couldn't read ${File("pubspec.yaml").absolute.path} for configuration defaults! Using built-in defaults. Override with @Artifact(...) annotations only.",
    );
    artifactConfig = {"name": "couldnt_find_pubspec"};
  }

  assert(step.inputId.path == r'$lib$');
  registerDef("ArtifactCodecUtil");
  registerDef("ArtifactDataUtil");
  registerDef("ArtifactSecurityUtil");
  registerDef("ArtifactReflection");
  registerDef("ArtifactMirror");
  registerDef("Map<String,dynamic>");
  registerDef("List<String>");
  registerDef("String");
  registerDef("dynamic");
  registerDef("int");
  registerDef("ArtifactModelExporter");
  registerDef("ArgumentError");
  registerDef("Exception");
  List<ClassElement> artifacts = <ClassElement>[];

  await for (AssetId asset in step.findAssets($dartFilesInLib)) {
    if (!await step.resolver.isLibrary(asset)) continue;
    LibraryElement lib = await step.resolver.libraryFor(asset);

    for (Element e in lib.classes) {
      if (e is! ClassElement) continue;
      $iClassMap[e.name ?? ""] = e;
      if (!$artifactChecker.hasAnnotationOf(e, throwOnUnresolved: false)) {
        continue;
      }

      registerDef(e.name ?? "");

      artifacts.add(e);

      InterfaceType? supType = e.supertype;
      while (supType != null) {
        ClassElement sup = supType.element as ClassElement;
        if ($artifactChecker.hasAnnotationOf(sup, throwOnUnresolved: false)) {
          $linkSubclass(e, sup);
        }
        supType = supType.element.supertype;
      }
    }
  }

  Set<Uri> imports = <Uri>{};
  List<StringBuffer> classBuffers = <StringBuffer>[];
  List<Future<void>> work = <Future<void>>[];
  List<String> codecs = [];

  for (ClassElement art in artifacts) {
    imports.add(art.library.uri);
    work.add(
      generate(art, step).then((v) {
        imports.addAll(v.$1);
        classBuffers.add(v.$2);
      }),
    );

    codecs.addAll(
      $codecChecker
          .annotationsOf(art, throwOnUnresolved: false)
          .followedBy(
            art.fields.expand(
              (j) => $codecChecker.annotationsOf(j, throwOnUnresolved: false),
            ),
          )
          .followedBy(
            art.methods.expand(
              (j) => $codecChecker.annotationsOf(j, throwOnUnresolved: false),
            ),
          )
          .followedBy(
            art.constructors.expand(
              (j) => $codecChecker.annotationsOf(j, throwOnUnresolved: false),
            ),
          )
          .map((i) => i.getField("c"))
          .whereType<DartObject>()
          .map((i) {
            imports.add($getImport(i.type as InterfaceType, art.library));

            return i.type?.getDisplayString(withNullability: false);
          })
          .whereType<String>()
          .unique
          .map((i) {
            registerDef(i);
            return "${applyDefsF(i)}()";
          }),
    );
  }

  String codecRegistry = "const ${applyDefsF("int")} _=0;";

  registerDef("ArtifactAccessor");
  StringBuffer sb = StringBuffer();
  sb.write("${applyDefsF("int")} _ = ((){");
  if (codecs.isNotEmpty) {
    sb.write(
      "${applyDefsF("ArtifactCodecUtil")}.r(const [${codecs.join(",")}]);",
    );
  }

  sb.write(
    "if(!${applyDefsF("ArtifactAccessor")}.\$i(${stringD(step.inputId.package)})){${applyDefsF("ArtifactAccessor")}.\$r(${stringD(step.inputId.package)},${applyDefsF("ArtifactAccessor")}(isArtifact: \$isArtifact,artifactMirror:${artifacts.any((c) => $artifactChecker.firstAnnotationOf(c, throwOnUnresolved: false)?.getField("reflection")?.toBoolValue() ?? false) ? "\$artifactMirror" : "{}"},constructArtifact:\$constructArtifact,artifactToMap:\$artifactToMap,artifactFromMap:\$artifactFromMap));}",
  );
  sb.write("return 0;");
  sb.writeln("})();");
  codecRegistry = sb.toString();
  StringBuffer rbuf = StringBuffer();
  if (artifacts.any(
    (c) =>
        $artifactChecker
            .firstAnnotationOf(c, throwOnUnresolved: false)
            ?.getField("reflection")
            ?.toBoolValue() ??
        false,
  )) {
    registerDef("\$AClass");
    rbuf.write(
      "Map<Type,${applyDefsF("\$AClass")}> get \$artifactMirror => {",
    );

    for (ClassElement i in artifacts) {
      if ($artifactChecker
              .firstAnnotationOf(i, throwOnUnresolved: false)
              ?.getField("reflection")
              ?.toBoolValue() ??
          false) {
        registerDef(applyDefsF(i.supertype!.element.name ?? ""));

        for (String i in i.interfaces.map((i) => i.element.name ?? "")) {
          registerDef(i);
        }

        for (String i in i.mixins.map((i) => i.element.name ?? "")) {
          registerDef(i);
        }

        for (InterfaceType i in i.allSupertypes) {
          imports.add(i.element.library.uri);
        }

        registerDef("\$AClass<${i.name ?? ""}>");
        rbuf.write(applyDefsF(i.name ?? ""));
        rbuf.write(":");
        rbuf.write(applyDefsF("\$AClass<${i.name ?? ""}>("));
        rbuf.write("\$${i.name}.\$annotations,");
        rbuf.write("\$${i.name}.\$fields,");
        rbuf.write("\$${i.name}.\$methods,");
        rbuf.write("()=>\$${i.name}.newInstance,");
        rbuf.write("${applyDefsF(i.supertype!.element.name ?? "")},");
        rbuf.write(
          "[${i.interfaces.map((i) => applyDefsF(i.element.name ?? "")).join(",")}],",
        );
        rbuf.write(
          "[${i.mixins.map((i) => applyDefsF(i.element.name ?? "")).join(",")}],",
        );
        rbuf.write("${typeDescriptorCode(i.thisType, this)},");
        rbuf.write("),");
      }
    }

    rbuf.writeln("};");
  }

  /// int _v = _register();
  //
  // int _register() {
  //   ArtifactAccessor.register(
  //     "something",
  //     ArtifactAccessor(
  //       isArtifact: $isArtifact,
  //       artifactMirror: $artifactMirror,
  //       constructArtifact: $constructArtifact,
  //       artifactToMap: $artifactToMap,
  //       artifactFromMap: $artifactFromMap,
  //     ),
  //   );
  //   return 0;
  // }

  await Future.wait(work);
  registerDef("List<dynamic>");
  imports.add(Uri.parse("package:artifact/artifact.dart"));
  StringBuffer outBuf =
      StringBuffer()
        ..writeln('// GENERATED – do not modify by hand\n')
        ..writeln(
          [
            "camel_case_types",
            "non_constant_identifier_names",
            "constant_identifier_names",
            "library_private_types_in_public_api",
            "unused_element",
          ].map((i) => "// ignore_for_file: $i").join("\n"),
        )
        ..writeln(
          imports
              .where((i) => i.toString().trim().isNotEmpty)
              .map((i) => 'import "$i";')
              .toSet()
              .toList()
              .join(),
        )
        ..writeln(
          compression
              ? defs.entries.map((i) => "typedef ${i.key}=${i.value};").join()
              : "",
        )
        ..writeln(
          "${applyDefsF("ArgumentError")} __x(${applyDefsF("String")} c,${applyDefsF("String")} f)=>${applyDefsF("ArgumentError")}('\${${stringD("Missing required ")}}\$c.\$f');",
        )
        ..write(
          compression
              ? "const ${applyDefsF("List<String>")} _S=[${strDD.map((i) => "'$i'").join(",")}];"
              : "",
        )
        ..write(
          compression
              ? "const ${applyDefsF("List<dynamic>")} _V=[${valDD.map((i) => " $i ".replaceAll(" const ", "").trim()).join(",")}];"
              : "",
        )
        ..write("const ${applyDefsF("bool")} _T=true;")
        ..write("const ${applyDefsF("bool")} _F=false;")
        ..writeln(codecRegistry);

  StringBuffer mainBuf = StringBuffer();
  for (StringBuffer cb in classBuffers) {
    mainBuf.writeln(cb);
  }

  String r = mainBuf.toString();
  outBuf.writeln(r);

  if (artifacts.isEmpty) {
    outBuf.writeln("bool \$isArtifact(dynamic v)=>false;");
  } else {
    outBuf.write(
      "bool \$isArtifact(dynamic v)=>v==null?false : v is! Type ?\$isArtifact(v.runtimeType):",
    );

    for (ClassElement i in artifacts) {
      outBuf.write(
        "v == ${applyDefsF(i.name ?? "")} ${i == artifacts.last ? "" : "||"}",
      );
    }

    outBuf.writeln(";");
  }

  outBuf.write(rbuf);

  if (artifacts.isEmpty) {
    outBuf.writeln(
      "T \$constructArtifact<T>() => throw ${applyDefsF("Exception")}();",
    );
  } else {
    outBuf.write("T \$constructArtifact<T>() => ");

    for (ClassElement i in artifacts) {
      outBuf.write(
        "T==${applyDefsF(i.name ?? "")} ?\$${(i.name)}.newInstance as T ${i == artifacts.last ? "" : ":"}",
      );
    }

    outBuf.writeln(": throw ${applyDefsF("Exception")}();");
  }

  if (artifacts.isEmpty) {
    outBuf.writeln(
      "${applyDefsF("Map<String,dynamic>")} \$artifactToMap(Object o)=>throw ${applyDefsF("Exception")}();",
    );
  } else {
    outBuf.write(
      "${applyDefsF("Map<String,dynamic>")} \$artifactToMap(Object o)=>",
    );

    for (ClassElement i in artifacts) {
      outBuf.write(
        "o is ${applyDefsF(i.name ?? "")} ?o.toMap()${i == artifacts.last ? "" : ":"}",
      );
    }

    outBuf.writeln(":throw ${applyDefsF("Exception")}();");
  }

  if (artifacts.isEmpty) {
    outBuf.writeln(
      "T \$artifactFromMap<T>(${applyDefsF("Map<String,dynamic>")} m)=>throw ${applyDefsF("Exception")}();",
    );
  } else {
    outBuf.write(
      "T \$artifactFromMap<T>(${applyDefsF("Map<String,dynamic>")} m)=>",
    );

    for (ClassElement i in artifacts) {
      outBuf.write(
        "T==${applyDefsF(i.name ?? "")} ?\$${i.name}.fromMap(m) as T${i == artifacts.last ? "" : ":"}",
      );
    }

    outBuf.writeln(":throw ${applyDefsF("Exception")}();");
  }
  AssetId out = AssetId(step.inputId.package, 'lib/gen/artifacts.gen.dart');
  await step.writeAsString(out, outBuf.toString());

  bool autoExport = artifactConfig["export"] ?? false;
  Stream<AssetId> assets = step.findAssets(Glob('lib/**.dart'));

  List<Future<String?>> worker = [];
  await for (AssetId i in assets) {
    String exportUri = i.uri.path;

    if (exportUri != '${artifactConfig["name"]}/gen/exports.gen.dart' &&
        exportUri !=
            '${artifactConfig["name"]}/${artifactConfig["name"]}.dart') {
      bool readable = await step.canRead(i);
      if (!readable) {
        verbose("Skipping unreadable export analysis for: ${i.uri}");
        continue;
      }

      worker.add(
        step.readAsString(i).then((v) {
          Iterable<SyntacticEntity> ast =
              parseString(content: v).unit.childEntities;

          for (SyntacticEntity j in ast) {
            if (j is PartOfDirective) {
              return null;
            }
          }

          return getExpString(ast, exportUri, autoExport);
        }),
      );
    } else {
      verbose("Skipping export analysis for: ${i.uri}");
    }
  }

  List<String> s = (await Future.wait(worker)).whereType<String>().toList();
  s.add("export 'artifacts.gen.dart';");
  if (s.isNotEmpty) {
    AssetId outExports = AssetId(
      step.inputId.package,
      'lib/gen/exports.gen.dart',
    );
    await step.writeAsString(outExports, s.join("\n"));
  } else {
    warn("No exports generated.");
  }
}