Skip to content

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.