Phases¶
Phases apply only to templates (provider-owned .jinja files), so this page
is for provider developers. Directives never run on project-owned files: those
are filled by Insertions, which have no phase concept (they run
in a single pass after rendering, once files exist on disk).
Directives are processed in two phases:
pre-render(default): runs on staged templates, before Jinja2.after-render(opt-in): runs on rendered files, after Jinja2.
All processed directive lines are stripped from the final output, so project files stay clean either way.
Why two phases?¶
A directive written literally in a template can always be found before
rendering. But a directive that lives inside Jinja-generated content (a loop
body, a conditional block) doesn't exist until Jinja2 has produced the final
file content. Such a directive must declare the after-render phase so repolish
evaluates it on the rendered output instead.
## repolish:keep-block[user-note|after-render] start="<!-- note-start -->" end="<!-- note-end -->"
<!-- note-start -->
{% for item in items %}
- {{ item }}
default note for {{ item }}
<!-- note-end -->
{% endfor %}
Each rendered repetition carries its own keep block, and each one is reconciled against the developer's current file after rendering.
The |after-render suffix¶
Append |after-render to the directive name inside the square brackets:
With no suffix the directive runs in pre-render; that is the default for every
directive family.
The suffix works on all directive families except tag blocks:
repolish:regex[...]repolish:multiregex-block[...]/repolish:multiregex[...]repolish:keep-block[...]/repolish:keep-rest[...]/repolish:keep-header[...]
Tag blocks (repolish:start[...] / repolish:end[...]) are anchors-driven and
are always replaced before Jinja2 runs: content injected after rendering
would never be seen by the template engine.
Invalid suffixes¶
An unrecognized suffix ([notes|after_rendr], [notes|post-render]) logs a
warning and falls back to pre-render: the directive is never silently dropped.
Worked example¶
After-render pays off when the values in the rendered file are assembled from
merged context. Say a mise provider asks the other providers which tools they
need, then renders them with default versions, while the developer has pinned
real versions in their current mise.toml.
One way to preserve those versions is to read the developer's file in Python, during context creation, and seed the versions before rendering:
# provider code: reconciliation hidden in context assembly
current_versions = parse_mise_toml(project_root / 'mise.toml')
tools = {name: current_versions.get(name, default) for ...}
That works, but the reconciliation is invisible in the template and every
provider rediscovers it. The declarative alternative: keep the provider code
deciding which tools ship (context, legitimately provider logic), and let an
|after-render multiregex directive preserve the versions: Jinja renders
the provider's tool list with defaults, then the directive pulls each version
from the developer's existing file:
# repolish/mise.toml.jinja
[tools]
## repolish:multiregex-block[tools|after-render] ^\[tools\](.*?)(?=\n\[|\Z)
## repolish:multiregex[tools|after-render] ^(")?([^"=\s]+)(")?\s*=\s*"([^"]+)"$
{% for tool, default in tools.items() %}
{{ tool }} = "{{ default }}"
{% endfor %}
No provider code reads project files; the template itself declares what
survives. For a single value (a version line, an author field) the same idea
uses repolish:regex[name|after-render] instead of the block pair.
The full pipeline¶
An apply is one run of the repolish apply command over your project (see
repolish apply). During an apply, each provider-managed
file goes through this pipeline in order:
- Pre-render phase on staged templates:
- Tag blocks: replaced with provider-supplied anchor content
(
create_anchors()/anchors:inrepolish.yaml). - Keep directives: regions restored from the developer's file.
- Regex directives: lines adopted from the developer's file.
- Multiregex directives: structured blocks merged.
- Jinja2 rendering.
- After-render phase on rendered files: only directives carrying the
|after-rendersuffix; same family order as step 1. - Insertions are written to the files on disk.
- Post-process formatting (provider mode hooks, e.g. formatters).
Note what this means in practice: values captured in the pre-render phase are
substituted into the template before Jinja2 sees it, so a
repolish:regex-adopted line is what Jinja renders around, while an
|after-render directive operates on content that is already final.
!!! note "Phases for project-owned files?" Insertions already touch project
files in what is effectively the after-render moment (once generated files exist
on disk), so a literal |after-render suffix there would add nothing. What is
coming is the other half of that picture: insertion zones (the planned
repolish-insert directive, see the
quadrant diagram), where a provider declares a
fillable zone in a template. Its zones survive rendering into the generated
file, and the insertion phase fills them, so the after-render phase is where
those declarations will be parsed.