Skip to content

ajinasokan/dartgen

Repository files navigation

DartGenerate

An inline generator collection for Dart. Generate JSON serializers, enums with mapped values and iterables, export index etc. for your Flutter or Dart projects.

Unlike the standard code generators DartGenerate doesn't produce .g.dart files. Instead the code is inserted into the actual definition itself. This is very similar to how dartfmt works. This can also be compared to how Java IDEs generate the getters and setters for fields. The objective of this flow is to keep the project clean and make codebase more readable.

Features

  • Generate enums with serializers.
  • Generate model serializers. You can add enums inside these models as well.
  • Generate index from a directory. This file will export all the .dart files.
  • Embed files from a directory.
  • Watches for file changes.
  • Config file to setup multiple generators.
  • Option to enable recursive travel of dirs.

Install

From pub:

$ pub global activate dartgenerate

From git:

$ pub global activate --source git https://github.com/ajinasokan/dartgen

Make sure you set the $PATH for pub. To verify run:

$ dartgenerate

No dartgenerate.json found in this directory.

Example config:
{
  "dir": "lib",
  "generators": [
      { "dir": "lib/models", "type": "model" },
      { "dir": "lib/models", "type": "index" },
      { "dir": "lib/constants", "type": "constant" },
      { "dir": "lib/constants", "type": "index" },
  ],
}

To run in watch mode:

$ dartgenerate watch

Config

{
    "dir": "lib",
    "generators": [
        { "dir": "lib/models", "type": "model" },
        { "dir": "lib/models", "type": "index" },
        { "dir": "lib/constants", "type": "constant" },
        { "dir": "lib/constants", "type": "index" },
        { "dir": "lib/components", "type": "index", "recursive": true },
        { "dir": "lib/screens", "type": "index", "recursive": true },
        { "dir": "lib/svgs", "type": "embed" }
    ]
}

Explanation:

.dir directory to watch file changes. Run dartgenerate watch to take this into consideration.

.generators list of all generators to run. This can be model, constant, index or embed generators. You can have more than one kind of generator executing in same directory.

.generators.dir sets the directory to run a particular generator.

.generators.type sets the type of the generator to run in the above directory.

.generators.recursive sets whether to run this generator recursively in the directory.

An example of usage can be found here and in /test directory.

Code Generation

The generation process takes the code and the annotations inside it as the input and generates the appropriate code and injects back to the code. Clearly this is mutation of the code, just like how dartfmt is. Hence it is adviced to have your code version controlled, with git or by any other means, so that you can reverse if the generation caused any damage.

Generating models

Code you write should look like this:

import 'dart:convert';

@pragma('model')
class Address {

  @pragma('json:street_name')
  String streetName = "NA";

}

@pragma('model') tells DartGenerate to generate model for this class. This includes constructor, map conversion, json conversion and serialization methods. @pragma('json:street_name') on a field gives the alias for that field in the json. If you don't specify an alias like this it will not be part of the serialization. You can specify a default value for the field. This will be used if you didn't initialize the field, the field doesn't exist or if the field is null in the json.

You can use all the data types that are supported in json, lists and maps of them, other models and enums with serializers generated by DartGenerate and Decimal.

Optionally you can add @pragma('model', 'patchWith,clone') flags to generate field patching and cloning methods. This clone method only does shallow clones. If you have to do deep clones you can use toJson and fromJson methods.

You can keep additional methods in the models and they will be kept intact.

Generating enums

Define your enums like this:

@pragma('enum')
class Color {
  static const Red = Color('red');
  static const Green = Color('green');
}

DartGenerate wil generate the iterators, equivalence checks etc. These enums will be identified during the model generation. So they will be serialized properly.

Generating index of exports

If you have a really large project its easier if all the dart files are exported. Dart is very good at handling cyclic imports given there are no name collisions.

If you enable index generation in a directory by defining in the dartgenerate.json it will generate index.dart in that directory with:

export 'address.dart';
export 'person.dart';

If you are in watch mode this will be automatically updated on addition/deletion of files.

Embedding files

This is useful if you want to embedd small textual files like SVG, HTML, CSV etc. Embedding in the code instead of assets will help you access this synchronously and instantaneously. Bear in mind, if the file size is really big it will also increase the app's binary size which can result in slower load times.

You can enable embedding generator in dartgenerate.json for a directory and it will generate index.dart with:

final eulatxt = 'MIT License';
final imagesvg =
    '<!DOCTYPE html>\n<html>\n<body>\n\n<svg height=\"100\" width=\"100\">\n  <circle cx=\"50\" cy=\"50\" r=\"40\" stroke=\"black\" stroke-width=\"3\" fill=\"red\" />\n  Sorry, your browser does not support inline SVG.  \n</svg> \n \n</body>\n</html>\n';

License

DartGenerate is licensed under the MIT license