Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to run a code-generator on the top of another code-generator?

Tags:

flutter

dart

Using the source_gen stack to make a code generator, how can I make a generator that generates code that would be the input of another generator (more specifically json_serializable)?

For example, consider:

class Example extends Generator {
  @override
  String generate(LibraryReader library, BuildStep buildStep) {
    return '''
@JsonSerializable(nullable: false)
class Person {
  final String firstName;
  final String lastName;
  final DateTime dateOfBirth;
  Person({this.firstName, this.lastName, this.dateOfBirth});
  factory Person.fromJson(Map<String, dynamic> json) => _PersonFromJson(json);
  Map<String, dynamic> toJson() => _PersonToJson(this);
}
''';
  }
}

This is an example of a code-generator that output code which then needs to be sent to json_serializable

What can I do so that json_serializable correctly generates here?

like image 382
Rémi Rousselet Avatar asked Dec 27 '19 17:12

Rémi Rousselet


2 Answers

Check the build.yaml config file documentation for more info, but I think you should use the applies_builders param that allows to execute another build after the defined one.

The example shows a builder that generates .tar.gz files and then executes another build that takes the .tar.gz files as input

builders:
  # The regular builder config, creates .tar.gz files.
  regular_builder:
    import: "package:my_package/builder.dart"
    builder_factories: ["myBuilder"]
    build_extensions: {".dart": [".tar.gz"]}
    auto_apply: dependents
    apply_builders: [":archive_extract_builder"]
post_process_builders:
  # The post process builder config, extracts .tar.gz files.
  extract_archive_builder:
    import: "package:my_package/extract_archive_builder.dart"
    builder_factory: "myExtractArchiveBuilder"
    input_extensions: [".tar.gz"]

so with source_gen you should implement for your build

applies_builders: ["source_gen|combining_builder", "json_serializable"]

and configure the other builder

json_serializable:
    import: "package:json_serializable/builder.dart"
    builder_factories: ["jsonSerializable"]
    build_extensions: {".dart": ["json_serializable.g.part"]}
    auto_apply: dependents
    build_to: cache
    applies_builders: ["source_gen|combining_builder"]
like image 174
jamesblasco Avatar answered Oct 07 '22 21:10

jamesblasco


It's not possible just with annotation because there maybe two packages that both have the @JsonSerializable annotation

There are two situtations :

  • You know what other generators should run after your generator.

    • https://stackoverflow.com/a/59605830/6877472 is one of the solutions
    • you can use the other generator's code in your own generator and call their generator's.generate function. example code:

class Example extends Generator {
    @override
    String generate(LibraryReader library, BuildStep buildStep) {
      return JsonSerializable().generate('''
          @JsonSerializable(nullable: false)
          class Person {
            final String firstName;
            final String lastName;
            final DateTime dateOfBirth;
            Person({this.firstName, this.lastName, this.dateOfBirth});
            factory Person.fromJson(Map<String, dynamic> json) => _PersonFromJson(json);
            Map<String, dynamic> toJson() => _PersonToJson(this);
          }
        ''');
     }

}

  • You don't know what other generators should run after your generator.

Unfortunately currently there is no way to tell the source_gen that your generator may produce a code that needs code generation.

I created an issue here https://github.com/dart-lang/source_gen/issues/442 if you want to subscribe

like image 30
Sahandevs Avatar answered Oct 07 '22 21:10

Sahandevs