Pony Gets a Template Engine If you’ve been following along, you know the story. Lori for networking. Stallion for HTTP. JSON back in the standard library. Pony’s web development stack is coming together piece by piece. But a web stack that only speaks JSON is an API server with aspirations. At some point, someone is going to want an actual page. ponylang/templates handles that. The basics The syntax is what you’d expect if you’ve used Mustache or Jinja. Variables go in {{ }}. You get conditionals, loops, filters with pipes, includes, template inheritance, whitespace control, comments. The feature set is table stakes for a modern template engine and I’m not going to walk through each piece. The examples cover all of it. What I want to dig into is what isn’t table stakes. Two template types, one syntax Templates has two entry points: Template and HtmlTemplate. Same syntax. Same parser. Same features. The difference is what happens when a variable gets rendered. Template renders values as-is. No escaping, no transformation. Config files, emails, code generation, anything that isn’t HTML. HtmlTemplate applies context-aware escaping to every variable based on where it appears in the HTML structure. That phrase “context-aware” is doing a lot of work, and it’s the interesting part. HTML escaping that actually works Most template engines handle XSS prevention with a single escape function. Every variable gets HTML entity encoding: < becomes <, > becomes >, & becomes &. Call it a day. That works when every variable lands between tags in text content. It doesn’t work everywhere else. Consider this template: <a href="{{ url }}">{{ label }}</a> <button onclick="{{ handler }}">Click</button> <div style="color: {{ color }}">{{ text }}</div> Four variables, three different HTML contexts. {{ label }} and {{ text }} sit in text content. Entity encoding handles them fine. But {{ url }} is in a URL attribute. Entity encoding won’t stop javascript:alert('xss') from executing,...
First seen: 2026-03-23 22:12
Last seen: 2026-03-24 15:31