std/template es el motor de render puro: sin red y sin E/S. tpl_render(tmpl, ctx) recibe un string de template y un contexto Map, y devuelve el texto final, así que sirve tanto por su cuenta como detrás de un servidor HTTP. Esta receta lo ejercita sola y verifica cada capacidad con assert, de modo que el programa es su propio test.
// Template engine, Flask-style — {{}}, {{{raw}}}, #if, #each, partials // Motor de templates estilo Flask — interpolación, condicionales, loops, partials // // std/template is the PURE engine (no networking): tpl_render(tmpl, ctx) // takes a template string and a Map<String> context and returns the final // text. It works standalone (this example) or behind std/serve (see // examples/by-example/78-84, which render real HTTP responses with it). import "std/template" fn main() -> int { // ── Escaped vs. raw interpolation ─────────────────────────────────── // {{key}} HTML-escapes the value (XSS-safe by default); {{{key}}} does // NOT escape — only use it with trusted/pre-sanitized content. var ctx1: Map = map_new() ctx1.insert("name", "<script>alert(1)</script>") let out1: String = tpl_render("hola {{name}}", ctx1) assert(out1 == "hola <script>alert(1)</script>", "{{key}} escapa por defecto") var ctx2: Map = map_new() ctx2.insert("html", "<b>negrita</b>") let out2: String = tpl_render("{{{html}}}", ctx2) assert(out2 == "<b>negrita</b>", "{{{key}}} no escapa (raw)") // ── #if / else ─────────────────────────────────────────────────── // truthy = la key existe Y su valor no es "" ni "0". var ctx3: Map = map_new() ctx3.insert("logged_in", "1") let out3: String = tpl_render("{{#if logged_in}}bienvenido{{else}}invitado{{/if}}", ctx3) assert(out3 == "bienvenido", "#if con valor truthy") var ctx4: Map = map_new() ctx4.insert("logged_in", "0") let out4: String = tpl_render("{{#if logged_in}}bienvenido{{else}}invitado{{/if}}", ctx4) assert(out4 == "invitado", "#if: \"0\" cuenta como falsy") // ── #each ─────────────────────────────────────────────────────── // La key debe resolver a un Array<Map>; el body se renderiza una vez // por elemento, usando ese Map como contexto del bloque (soporta // anidar {{#each}} dentro de {{#each}}). var it1: Map = map_new() it1.insert("name", "uno") var it2: Map = map_new() it2.insert("name", "dos") let items: Array = [it1, it2] var ctx5: Map = map_new() ctx5.insert("items", items) let out5: String = tpl_render("<ul>{{#each items}}<li>{{name}}</li>{{/each}}</ul>", ctx5) assert(out5 == "<ul><li>uno</li><li>dos</li></ul>", "#each renderiza un <li> por item") // ── Partials ─────────────────────────────────────────────────── // tpl_partial registra un template reusable por nombre; {{> name}} lo // incluye renderizado con el contexto ACTUAL (no uno nuevo ni vacío). tpl_partial("footer", "<footer>{{year}}</footer>") var ctx6: Map = map_new() ctx6.insert("year", "2026") let out6: String = tpl_render("<body>hola{{> footer}}</body>", ctx6) assert(out6 == "<body>hola<footer>2026</footer></body>", "{{> name}} incluye el partial") // ── Todo junto: una página real ─────────────────────────────────── var page_ctx: Map = map_new() page_ctx.insert("title", "Nyx & serve") page_ctx.insert("items", items) let page_tmpl: String = "<h1>{{title}}</h1><ul>{{#each items}}<li>{{name}}</li>{{/each}}</ul>" let page: String = tpl_render(page_tmpl, page_ctx) assert(page == "<h1>Nyx & serve</h1><ul><li>uno</li><li>dos</li></ul>", "página completa: título + loop") print("OK: motor de templates (interpolación, #if, #each, partial) verificado end-to-end") return 0 }
OK: motor de templates (interpolación, #if, #each, partial) verificado end-to-end
Cómo funciona
{{key}} escapa el valor como HTML, y eso es lo que hace que la interpolación sea segura contra XSS por defecto: el contexto guarda <script>alert(1)</script> y el render produce las entidades escapadas. {{{key}}} no escapa y solo se usa con contenido en el que ya confías o que ya sanitizaste.
{{#if key}}…{{else}}…{{/if}} considera verdadera una clave cuando existe y su valor no es vacío ni "0" — la receta verifica las dos ramas. {{#each key}} espera que la clave resuelva a un array de Map y renderiza su cuerpo una vez por elemento, usando ese elemento como contexto del bloque, que es lo que permite anidar un {{#each}} dentro de otro.
tpl_partial(name, tmpl) registra un fragmento reusable por nombre y {{> name}} lo incluye renderizado con el contexto ACTUAL, no con uno nuevo ni vacío. Todas las aserciones se cumplen, así que la única línea de salida es la prueba completa: escapado, condicionales, loops y partials se comportan como está documentado.