Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generate one file for a list of parsed files using source_gen in dart

Tags:

dart

I have a list of models that I need to create a mini reflective system.
I analyzed the Serializable package and understood how to create one generated file per file, however, I couldn't find how can I create one file for a bulk of files.

So, how to dynamically generate one file, using source_gen, for a list of files?

Example:
Files
user.dart
category.dart

Generated:
info.dart (containg information from user.dart and category.dart)

like image 821
Jonathan Avatar asked Oct 30 '22 07:10

Jonathan


2 Answers

Found out how to do it with the help of people in Gitter.
You must have one file, even if empty, to call the generator. In my example, it is lib/batch.dart.

source_gen: ^0.5.8

Here is the working code:

The tool/build.dart

import 'package:build_runner/build_runner.dart';
import 'package:raoni_global/phase.dart';

main() async {
  PhaseGroup pg = new PhaseGroup()
    ..addPhase(batchModelablePhase(const ['lib/batch.dart']));

  await build(pg,
      deleteFilesByDefault: true);
}

The phase:

batchModelablePhase([Iterable<String> globs =
const ['bin/**.dart', 'web/**.dart', 'lib/**.dart']]) {
  return new Phase()
    ..addAction(
        new GeneratorBuilder(const
        [const BatchGenerator()], isStandalone: true
        ),
        new InputSet(new PackageGraph.forThisPackage().root.name, globs));
}

The generator:

import 'dart:async';

import 'package:analyzer/dart/element/element.dart';
import 'package:build/build.dart';
import 'package:source_gen/source_gen.dart';
import 'package:glob/glob.dart';
import 'package:build_runner/build_runner.dart';

class BatchGenerator extends Generator {
  final String path;

  const BatchGenerator({this.path: 'lib/models/*.dart'});

  @override
  Future<String> generate(Element element, BuildStep buildStep) async {
    // this makes sure we parse one time only
    if (element is! LibraryElement)
      return null;

    String libraryName = 'raoni_global', filePath = 'lib/src/model.dart';
    String className = 'Modelable';

    // find the files at the path designed
    var l = buildStep.findAssets(new Glob(path));

    // get the type of annotation that we will use to search classes
    var resolver = await buildStep.resolver;
    var assetWithAnnotationClass = new AssetId(libraryName, filePath);
    var annotationLibrary = resolver.getLibrary(assetWithAnnotationClass);
    var exposed = annotationLibrary.getType(className).type;

    // the caller library' name
    String libName = new PackageGraph.forThisPackage().root.name;

    await Future.forEach(l.toList(), (AssetId aid) async {
      LibraryElement lib;

      try {
        lib = resolver.getLibrary(aid);
      } catch (e) {}

      if (lib != null && Utils.isNotEmpty(lib.name)) {
        // all objects within the file
        lib.units.forEach((CompilationUnitElement unit) {
          // only the types, not methods
          unit.types.forEach((ClassElement el) {
            // only the ones annotated
            if (el.metadata.any((ElementAnnotation ea) =>
            ea.computeConstantValue().type == exposed)) {
              // use it
            }
          });
        });
      }
    });

    return '''
       $libName
    ''';
  }
}
like image 182
Jonathan Avatar answered Nov 15 '22 06:11

Jonathan


It seems what you want is what this issue is about How to generate one output from many inputs (aggregate builder)?

like image 25
Günter Zöchbauer Avatar answered Nov 15 '22 06:11

Günter Zöchbauer