Templates
The files a module adds to an app are mason templates, bundled into Dart, and the pipeline renders them in memory with mustache. A template can use the variables below, and the pipeline checks the rules that follow. Most of these rules exist so that a module never breaks an app it cannot see.
Variables
A template can read:
| Variable | Value |
|---|---|
app_name | The package name of the app, such as my_app. mason's case lambdas work on it: {{app_name.titleCase()}} is My App. |
org_name | The organization, such as com.example. |
has_<role> | true when the role is present. It is set for the roles the brick's owner provides, requires or uses, and for the app entry. |
| socket tags | The rendered contributions of a socket, such as {{{smf_app_entry__bootstrap_late}}}. |
| variables of render hooks | What the owner's render hooks return in RoleOutput.vars. |
| the brick's own variables | The vars of its BrickContribution. |
Mustache renders a variable that none of these sets as nothing, so the pipeline reports it. The brick's own variables and the variables of render hooks cannot take the names that the pipeline or mason sets: app_name, org_name, smf_…, has_…, the case lambdas of mason, such as snakeCase, and its brace variables, __LEFT_CURLY_BRACKET__ and __RIGHT_CURLY_BRACKET__.
Mustache escapes a variable in two braces for HTML, so code and paths with a slash go in three: {{{name}}}.
Presence flags
Code that refers to a role the module only uses goes inside the section of its presence flag. flutter_core switches the root widget on the router this way:
MaterialApp{{#has_router}}.router{{/has_router}}(
title: '{{app_name.titleCase()}}',
{{#has_router}}routerConfig: appRouter.config,{{/has_router}}{{^has_router}}home: const FallbackStartScreen(),{{/has_router}}
An import that only one branch needs goes into the same section:
{{#has_router}}import 'core/router/app_router.dart';{{/has_router}}{{^has_router}}import 'core/app/fallback_start_screen.dart';{{/has_router}}
A flag is set only for the roles the owner declares, and the pipeline reports a section over a flag it does not set.
Socket tags
A template marks a socket with its tag in three braces, {{{smf_<owner>__<name>}}}:
- A tag stands outside mustache sections. The socket renders whatever the app has, and its contributions carry their own
when. - A socket whose contributions carry imports, such as a code socket, has its tag in exactly one Dart file, which gets the imports. Only the tag of a socket for one value, such as the minimum iOS version, may appear several times.
- Both tags of a wrapper,
…_openand…_close, are in the same file, the opening one first. - The tag of a socket that renders whole lines, such as the native sockets and the README sections of the app entry, stands alone at the start of its line.
- A tag must not come right after a
{, which mustache would read as part of the tag. Put the parameters of a constructor on lines of their own, as the tags of parameter annotations need. - A line that holds nothing but a tag that gets nothing goes away with it, so an empty socket leaves no blank line.
The owner of a socket must hold its tag in its templates: the role's template or its provider for a socket of a role, and the module for a socket of its own. The tag of a member of a family, such as the annotations of a screen, goes into the file of the module whose data creates it.
Variables of render hooks
A render hook returns variables for the bricks of its owner in RoleOutput.vars. They are plain data, such as strings, numbers, booleans and lists and maps of them, or a Fragment of code with imports:
- A template reads a fragment variable as it is,
{{{routes}}}, outside mustache sections. A path cannot read one. - A fragment variable with imports can be read only by a Dart library, not by a part file or a file that is not Dart. The pipeline adds the imports to every file of the owner that reads it.
- A line that holds nothing but a fragment variable without code goes away.
- A fragment variable that no template of its owner reads is an error, since its code would be lost.
- A variable that two hooks of one owner, or a hook and a brick of the owner, both set is an error.
Imports
A fragment never imports anything itself. It lists the imports it needs, and the pipeline adds them to the file that holds the tag, merged with the imports of other fragments and sorted. ImportRef.app('core/router/app_router.dart') imports a file of the app by its path below lib/. After rendering, the pipeline runs dart fix for unused and duplicate imports, so a template may import a library that only some of its branches use.
Paths
The path of a file in a brick is its path in the app. A path may use variables, such as the directory of the Kotlin package of the app, {{{android_package_path}}}, but no mustache sections or partials. Files that only some apps get go into a brick of their own, which the module contributes with when or in a variant.
Every file of the app has exactly one owner, so two bricks must not generate the same path. A brick must not contain files that belong to one machine or one build, such as .dart_tool/, build/, GeneratedPluginRegistrant files, .iml files or signing keys, and the pipeline rejects them.
What mason would change
- mason removes a backslash that comes right before a line break or a non-ASCII character, in templates and in everything it renders into them. The pipeline rejects such a backslash in fragments and variables.
- The pipeline rejects a template that changes the mustache delimiters or includes a partial.
- mason copies a text file without any tag as it is, and a binary file too.
- Bricks have no hooks, and the pipeline rejects a bundle with mason hooks.