[Part 1] Code generation in Dart: the basics
Have you ever wonder what makes a software programmer a great software programmer? Think about it for a minute. The answers could be pretty much endless, and depending on who you ask, you will get different responses… but if you ask me, being a lazy programmer will be my answer ;)
Doing more with less is key to progress. No one likes repeating the same task over and over again. It is tedious, boring, and not creative at all. Humans are really bad at this. We often make mistakes when doing something repetitive. However, guess who is really good and effective doing all these tasks that humans do not enjoy? I hope we are thinking the same: COMPUTERS!
Code generation is key nowadays to get the job done, in the shortest amount of time possible. The idea is simple: for all those tedious tasks that you do over and over again in your code base, you can find the pattern behind, create a generator tool, run it and see the magic happen!
In the Android world, these tools are part of the bread and butter of every developer: Retrofit, Dagger, Room… but what about Dart? And more important, what do we need to build our own tools?
Dart and code generation: tools available
In order to create tools that auto generate code, you will probably need to use these two packages:
source_gen
This package provides an friendly API to auto generate code. It is a better abstraction of some low level Dart packages, such as analyzer or build. While it is not mandatory to use, definitely it will save you a lot of headaches.
source_gen provides two abstract generator classes, that can be considered visitor classes:
Generator:
when extending this class, every single element of your code will be visited, so you have full control over what to do with the node/element being visitied.GeneratorForAnnotation:
similar to a simple Generator, but in this case you will also give it an “annotation”, so every node annotated with the given annotation will be visited, and the rest of nodes that are not annotated will be ignored.
Part of the job will be also configuring the Builder
that will wrap your generator. You have these options:
- If you want to write a
partial
piece of code, then using aSharedPartBuilder
is your best option. “part” allows you to split a library into multiple Dart files. This will produce a file with the extension.g.dart
. - You can use
PartBuilder
if you still want to use apart
approach, but control the extension file, such as .my_file.dart
. - If you just want to write a standalone library that can be imported, use a
LibraryBuilder
.
We will see a practical example in the Part 2 of these series of articles.
build_runner
This tool allow us to run our generators during the development phase. It can be invoked from the command line in this fashion:
pub run build_runner <command>
where <command>
can be:
build:
run a single build and exitwatch:
runs a daemon that will run on file changes and rebuilds if necessaryserve:
similar to watch, but also runs as a development servertest:
for testing purposes
In order to make it work in harmony with source_gen
, we will need to configure a build.yaml
file where we specify the different details of our generator configuration.
Who uses code generation in Dart?
When looking for inspiration and libraries that use code generation, you will immediately see some of the most famous packages available:
In the next article…
Now that we know the tools that we have available to auto generate code, in the next article of this series we will focus on a tiny project where we will use annotations and code generation keep track of all the TODOs in our application.
You can follow me on https://twitter.com/jcocaramos and see more code here on my public Github https://github.com/jorgecoca