Pipeline

Every save takes the same six steps, whether the code comes from a built-in macro, your Mustache template, or a macro you wrote in Dart:

flowchart LR
    source["Annotated Dart source"]
    parse["Parse with tree-sitter"]
    context["Build semantic context in Rust"]
    render["Render the selected Mustache template"]
    validate["Re-parse and validate the candidate"]
    splice["Splice the generated region"]
    write["Write the file atomically"]

    source --> parse --> context --> render --> validate --> splice --> write

Parse

tree-sitter parses your Dart. The //#region markers are found as real comments, so the same text inside a string literal is never mistaken for one.

Build context

dmx works out every decode, encode, equality, hash, copy and validation expression before any template runs. The template is handed finished strings, so it never has to reason about Dart types — which is what makes templates safe to edit.

Render

The Mustache template decides what the generated code looks like. Because the hard work is already done, swapping in your own template changes the shape of the output without changing what it means.

Validate

dmx parses the whole finished file, not just the part it generated. If that fails you get an error message and your file is left exactly as it was.

Emit

dmx checks that everything outside the generated region is unchanged, writes the new code into the region, skips the write entirely if nothing changed, and replaces the file in one step.

Only a bare //#region//#endregion block belongs to dmx. Put a label on one — //#region Helpers — and it is yours; dmx will not write into it.

The normative specification defines each stage and its diagnostics.