classDefinitionToSpec function

Spec classDefinitionToSpec(
  1. ClassDefinition definition,
  2. Iterable<FragmentClassDefinition> fragments,
  3. Iterable<ClassDefinition> classes
)

Generates a Spec of a single class definition.

Implementation

Spec classDefinitionToSpec(
  ClassDefinition definition,
  Iterable<FragmentClassDefinition> fragments,
  Iterable<ClassDefinition> classes,
) {
  final fromJson = definition.factoryPossibilities.isNotEmpty
      ? Constructor(
          (b) => b
            ..factory = true
            ..name = 'fromJson'
            ..requiredParameters.add(
              Parameter(
                (p) => p
                  ..type = refer('Map<String, dynamic>')
                  ..name = 'json',
              ),
            )
            ..body = Code(_fromJsonBody(definition)),
        )
      : Constructor(
          (b) => b
            ..factory = true
            ..name = 'fromJson'
            ..lambda = true
            ..requiredParameters.add(
              Parameter(
                (p) => p
                  ..type = refer('Map<String, dynamic>')
                  ..name = 'json',
              ),
            )
            ..body = Code('_\$${definition.name.namePrintable}FromJson(json)'),
        );

  final toJson = definition.factoryPossibilities.isNotEmpty
      ? Method(
          (m) => m
            ..name = 'toJson'
            ..annotations.add(const CodeExpression(Code('override')))
            ..returns = refer('Map<String, dynamic>')
            ..body = Code(_toJsonBody(definition)),
        )
      : Method(
          (m) => m
            ..name = 'toJson'
            ..lambda = true
            ..annotations.add(const CodeExpression(Code('override')))
            ..returns = refer('Map<String, dynamic>')
            ..body = Code('_\$${definition.name.namePrintable}ToJson(this)'),
        );

  final props = definition.mixins
      .map((i) {
        return fragments
            .firstWhere(
              (f) {
                return f.name == i;
              },
              orElse: () {
                throw MissingFragmentException(
                  i.namePrintable,
                  definition.name.namePrintable,
                );
              },
            )
            .properties
            .map((p) => p.name.namePrintable);
      })
      .expand((i) => i)
      .followedBy(definition.properties.map((p) => p.name.namePrintable));

  final extendedClass = classes.firstWhereOrNull(
    (e) => e.name == definition.extension,
  );

  return Class(
    (b) => b
      ..annotations.add(
        const CodeExpression(Code('JsonSerializable(explicitToJson: true)')),
      )
      ..name = definition.name.namePrintable
      ..mixins.addAll([
        refer('EquatableMixin'),
        ...definition.mixins.map((i) => refer(i.namePrintable)),
      ])
      ..methods.addAll([_propsMethod(props)])
      ..extend = definition.extension != null
          ? refer(definition.extension!.namePrintable)
          : refer('JsonSerializable')
      ..implements.addAll(definition.implementations.map(refer))
      ..constructors.add(
        Constructor((b) {
          if (definition.isInput) {
            b.optionalParameters.addAll(
              definition.properties
                  .where(
                    (property) =>
                        !property.isOverride && !property.isResolveType,
                  )
                  .map(
                    (property) => Parameter((p) {
                      p
                        ..name = property.name.namePrintable
                        ..named = true
                        ..toThis = true
                        ..required = property.type.isNonNull;
                    }),
                  ),
            );
          }
        }),
      )
      // Always add fromJson constructor (either delegating or traditional)
      ..constructors.add(fromJson)
      // Only add toJson method when not using GraphQLDataClass
      ..methods.add(toJson)
      ..fields.addAll(
        definition.properties.map((p) {
          if (extendedClass != null &&
              extendedClass.properties.any((e) => e == p)) {
            // if class has the same prop as in extension
            p.annotations.add('override');
          }

          final field = Field((f) {
            f
              ..name = p.name.namePrintable
              ..late = p.type.isNonNull
              ..type = refer(p.type.namePrintable)
              ..annotations.addAll(
                p.annotations.map((e) => CodeExpression(Code(e))),
              );
          });
          return field;
        }),
      ),
  );
}