Customizing Code Generation
AFib's provides two mechanisms for customizing code generation. The first is more relevant for applications, while the second is more appropriate for AFib libraries.
Customizing Source Templates from an Application
All generate
subcommands support an --export-templates
flag. When you add this flag to a generate subcommand, it will export the templates referenced by that command, instead of performing the command itself. For example, if you enter the command:
dart bin/xxx_afib.dart generate query ReadUserQuery --result-type User --export-templates
create core/files/simple_query.t_dart
Then no read_user_query.dart
file will be generated. Instead, the file generate/core/files/simple_query.t_dart
will be generated, as the output from the command states. Note that some commands rely on more than one template.
As a result, you can discover the templates you need to modify by running the generate
command for the code you are trying to generate. The --export-templates
will not overwrite files that already exist, so you can always re-run the export in order to remember which templates are contributing to the code you wish to modify.
What is in a .t_dart file?
A .t_dart file is just a .dart file which contains [!af...]
markup where values will be inserted. If you open one, you will find its syntax obvious.
Creating Specialized Templates
If you modify the exported templates, then your modifications will apply to all future generate
commands by default.
Instead, you might want to modify a template and use it only sometimes. In that case, you could make a copy of the original template with your own unique path, and then modify it. For example, you might copy the generate/core/files/simple_query.t_dart
file to generate/mycompany/files/query_variant.t_dart
.
Then, you can reference your custom template using the --override-templates
flag, which uses the =
symbol to indicate that you are assigning a special template to one of the default templates, like this:
dart bin/xxx_afib.dart generate query ReadUserQuery --result-type User --override-templates "generate/core/files/simple_query.t_dart=generate/mycompany/files/query_variant.t_dart"
In this case, AFib will use the syntax in the query_variant.t_dart
in place of the standard simple_query.t_dart
file. Note that the simple_query.t_dart
file need not exist on the file system for this to work.
If you have multiple templates to override, you can comma-separate them.
Creating Custom Project Styles
AFib's default project styles are files containing a series of AFib commands (e.g. generate), each of which references one or more --override-templates
which add the custom code that makes the project-style unique.
For example, here are some portions of the app-eval-demo
project style:
...
generate id ${insertAppNamespaceUpper}TestDataID.userWestCoast
....
generate ui ${insertAppNamespaceUpper}DefaultTheme --parent-theme AFFunctionalTheme --parent-theme-id ${insertAppNamespaceUpper}ThemeID.defaultTheme --override-templates "core/files/theme=project_styles/app-eval-demo/files/start_here_theme"
generate state CountHistoryItem --add-standard-root --member-variables "int id; int count;" --resolve-variables "User user;" --override-templates +
+core/files/model=project_styles/app-eval-demo/files/model_count_history_item
+core/files/model_root=project_styles/app-eval-demo/files/model_count_history_items_root
+core/snippets/define_test_data=project_styles/app-eval-demo/snippets/define_count_history_root_test_data
...
generate ui StartupScreen --no-back-button --override-templates +
+core/snippets/screen_build_with_spi_impl=project_styles/app-eval-demo/snippets/startup_screen_build_with_spi
+core/snippets/empty_screen_build_body_impl=project_styles/app-eval-demo/snippets/startup_screen_build_body
...
Note that you can use --export-templates
with the afib_bootstrap create
command. Doing so will export the project styles file in addition to the referenced templates.
Customizing Source from a Library
The code generation modifications above are appropriate for most applications. However, if you are writing a library and wish to generate code, you may wish to hook into AFib's source generation at a programmatic level.
Modifying code generation at a programmatic level has several benefits. First, your customizations are much more likely to continue working, even if the AFib syntax changes in the future. Second, AFib allows you to modify specific atomic pieces of code (snippets), rather than modifying only complete files. Finally, it does not depend on distributing .t_dart
files to end-users, as your customized template code is baked into your library.
As of this writing, I have not fully documented how to customize source code programmatically, but you can find an example in the afib_firebase_firestore
library, which modifies the existing query generation code in a way that is likely to continue to work even if the AFib's query code syntax changes in the future.