buildSchema method

FutureOr<void> buildSchema(
  1. BuildStep buildStep,
  2. AssetId schemaId,
  3. Glob generatedFiles,
  4. List<Glob> excludeFiles,
)

Implementation

FutureOr<void> buildSchema(BuildStep buildStep, AssetId schemaId,
    Glob generatedFiles, List<Glob> excludeFiles) async {
  /// BuiltValue classes with serializers. These will be added automatically
  /// using `@SerializersFor`.
  final builtClasses =
      SplayTreeSet<ClassElement>((a, b) => a.name.compareTo(b.name));

  /// Non BuiltValue classes with serializers (i.e. inline fragment classes).
  /// These need to be added manually since `@SerializersFor` only recognizes
  /// BuiltValue classes.
  final nonBuiltClasses =
      SplayTreeSet<ClassElement>((a, b) => a.name.compareTo(b.name));

  final hasSerializer = (ClassElement c) => c.fields.any((field) =>
      field.isStatic &&
      field.name == 'serializer' &&
      field.type.element?.name == 'Serializer' &&
      field.type.element?.source?.uri.toString() ==
          'package:built_value/serializer.dart');

  final isBuiltValue = (ClassElement c) => c.allSupertypes.any((interface) =>
      (interface.element.name == 'Built' ||
          interface.element.name == 'EnumClass') &&
      interface.element.source.uri.toString() ==
          'package:built_value/built_value.dart');

  final excludeFileIds = <String, AssetId>{};
  for (final excludeGlob in excludeFiles) {
    await for (final fileAssetId in buildStep.findAssets(excludeGlob)) {
      excludeFileIds[fileAssetId.path] = fileAssetId;
    }
  }

  await for (final input in buildStep.findAssets(generatedFiles)) {
    if (excludeFileIds.containsKey(input.path)) continue;
    final lib = await buildStep.resolver.libraryFor(input);
    lib.units
        .expand((cu) => cu.classes)
        .where((c) => hasSerializer(c) && isBuiltValue(c))
        .forEach(builtClasses.add);

    lib.units
        .expand((cu) => cu.classes)
        .where(
          (c) => hasSerializer(c) && !isBuiltValue(c),
        )
        .forEach(nonBuiltClasses.add);
  }

  final additionalSerializers = <Expression>{
    // GraphQL Operation serializer
    refer(
      'OperationSerializer',
      'package:gql_code_builder/src/serializers/operation_serializer.dart',
    ).call([]),
    // User-defined custom serializers
    ...config.customSerializers.map((ref) => ref.call([])),
    // Serializers from data classes that aren't caught by `@SerializersFor`
    ...nonBuiltClasses.map<Expression>(
      (c) => refer(c.name, c.source.uri.toString()).property('serializer'),
    ),
  };

  final library = buildSerializerLibrary(
    builtClasses,
    outputFileName.replaceFirst('.gql.dart', '.gql.g.dart'),
    additionalSerializers,
  );

  final allocator = PickAllocator(
    doNotPick: ['package:built_value/serializer.dart'],
    include: [
      'package:built_collection/built_collection.dart',
      'package:ferry_exec/ferry_exec.dart',
      ...config.typeOverrides.values.map((ref) => ref.url).whereType<String>()
    ],
  );

  final outputId = AssetId(
    schemaId.package,
    p.joinAll(pathSegments(schemaId)),
  );

  await writeDocument(outputId, library, allocator, buildStep);
}