entrypointContent method

String entrypointContent(
  1. Iterable<ConstructYaml> constructs, {
  2. required Directory root,
})

Implementation

String entrypointContent(
  Iterable<ConstructYaml> constructs, {
  required Directory root,
}) {
  const revaliConstruct = 'package:revali_construct/revali_construct.dart';
  const revali = 'package:revali/revali.dart';

  final conflicts = <String, List<String>>{};

  for (final yaml in constructs) {
    for (final construct in yaml.constructs) {
      (conflicts[construct.name] ??= []).add(yaml.packageName);
    }
  }

  final constructItems = [
    for (final yaml in constructs)
      for (final construct in yaml.constructs)
        refer('$ConstructMaker', revaliConstruct).newInstance(
          [],
          {
            'package': literalString(yaml.packageName),
            'isServer': refer('${construct.isServer}'),
            'isBuild': refer('${construct.isBuild}'),
            'optIn': refer('${construct.optIn}'),
            'name': literalString(construct.name),
            'hasNameConflict':
                literalBool((conflicts[construct.name] ?? []).length > 1),
            'maker': refer(
              construct.method,
              '${yaml.packageUri}${construct.path}',
            ),
          },
        ),
  ];

  final constructs0 = declareConst('_constructs')
      .assign(
        literalList(
          constructItems,
          refer('$ConstructMaker', revaliConstruct),
        ),
      )
      .statement;

  final path =
      declareConst('_root').assign(literalString(root.path)).statement;

  final main = Method(
    (b) => b
      ..name = 'main'
      ..returns = refer('void')
      ..modifier = MethodModifier.async
      ..requiredParameters.add(
        Parameter(
          (b) => b
            ..name = 'args'
            ..type = TypeReference(
              (b) => b
                ..symbol = ('List')
                ..types.add(refer('String')),
            ),
        ),
      )
      ..optionalParameters.add(
        Parameter(
          (b) => b
            ..name = 'sendPort'
            ..type = TypeReference(
              (b) => b
                ..symbol = 'SendPort'
                ..url = 'dart:isolate'
                ..isNullable = true,
            ),
        ),
      )
      ..body = Block.of([
        declareFinal('result')
            .assign(
              refer('run', revali).call([
                refer('args'),
              ], {
                'constructs': refer('_constructs'),
                'path': refer('_root'),
              }).awaited,
            )
            .statement,
        const Code('\n'),
        refer('sendPort')
            .nullSafeProperty('send')
            .call([refer('result')]).statement,
        const Code('\n'),
        refer('exitCode', 'dart:io').assign(refer('result')).statement,
      ]),
  );

  final library = Library(
    (b) => b.body.addAll(
      [
        constructs0,
        path,
        main,
      ],
    ),
  );

  final emitter = DartEmitter(
    allocator: Allocator.simplePrefixing(),
    useNullSafetySyntax: true,
  );
  try {
    final content = StringBuffer()
      ..writeln('// ignore_for_file: directives_ordering')
      ..writeln(library.accept(emitter));

    final clean = DartFormatter().format(content.toString());

    return clean;
  } on FormatterException {
    logger.err('Generated build script could not be parsed.\n'
        'This is likely caused by a misconfigured builder definition.');
    // TODO(mrgnhnt): throw custom exception
    throw Exception('Failed to generate build script');
  }
}