Skip to main content

ATL Reference

ATL (Archetect Template Language) is Archetect's template engine. It uses the familiar {{ expr }} / {% ... %} / {# ... #} delimiter family, but under the hood every template is compiled to a Lua function and executed in the same Lua VM as your archetype script. Logic blocks are real Lua (with ergonomic shorthands), and the data your script puts in the context flows into templates with zero conversion — nested tables, arrays, and all.

This section is the formal reference. For a guided introduction, see Templating with ATL.

PageContents
SyntaxExpressions, logic blocks, loops, conditionals, includes, comments, raw blocks, whitespace control
Filters & FunctionsEvery built-in filter/function, grouped by category, with examples

Where ATL Applies

SurfaceEntry pointNotes
File contentsdirectory.render(src, context)Every non-binary file in the tree is rendered. Binary files (detected by content inspection, not extension) are copied through untouched.
Single filesfile.render(src, context, opts)Same engine, one file.
Inline stringstemplate.render(str, context, opts?)Returns the rendered string, or writes to opts.destination. {% include %} is not available in inline strings.
File and directory namesautomatic during directory.renderA path segment is rendered only if it contains {{. A directory named {{ project-name }} becomes my-project. Names support the same expressions and filters as contents; {% include %} is not available in names.
Include partials{% include "partial.atl" %}Resolved at compile time against includes/ directories. See Syntax → Includes.

There is no special "template extension": the engine does not strip or require .atl on rendered files, and it never renames output files based on extension. By convention, .atl is used for partials that live in includes/ directories.

The Context Is Flat

Templates receive the context as a flat table. Variables are referenced bare — {{ project_name }}, never context.project_name:

{{ project-name }} is owned by {{ org_name }}.

Kebab-case keys like project-name work directly in expressions — the compiler recognizes them and resolves them against the context, so they don't collide with Lua's subtraction operator.

When a prompt or ctx:set declares cases (via the cases option), the value is stored under multiple casings at once — e.g. project-name, project_name, projectName, ProjectName, PROJECT_NAME — so templates can pick whichever casing the target file needs. See Templating with ATL for how case expansion is declared.

Nested data works too: your script can set entire model tables into the context and templates iterate them directly:

{% for field in entity.fields %}
pub {{ field.name | snake_case }}: {{ field.type }},
{% endfor %}

Read-Only Globals in Templates

Besides context variables and filters, templates can read three globals:

GlobalContents
archetectBinary/process info: version, version_major/minor/patch, is_offline, is_headless, locals_enabled, env (platform table)
archetypeCurrent archetype: description, directory, destination, authors, switches, answers(), mount_key(), is_library(), is_standalone(), include_path(rel)
formatPure (de)serialization helpers: to_json, to_yaml, to_toml, from_json, from_yaml, from_toml

Effectful script APIs (directory.render, git.*, shell.*, ...) are deliberately not reachable from templates — templates are pure.

Configuration

The templating: block in archetype.yaml controls engine behavior:

templating:
undefined: strict # lenient (default) | strict
trim_blocks: true # default: false
lstrip_blocks: true # default: false
OptionValuesDefaultEffect
undefinedlenient, strictlenientlenient: undefined variables render as empty. strict: any access to an undefined context variable raises a render-time error naming the missing key.
trim_blockstrue, falsefalseStrip the first newline after a {% ... %} block tag.
lstrip_blockstrue, falsefalseStrip leading whitespace on lines that contain only a block tag.

These options apply to file contents rendered via directory.render and file.render. Inline template.render strings and file/directory names always compile with the defaults (lenient, no block trimming).

Whitespace can also be controlled per-tag with - markers regardless of these settings — see Syntax → Whitespace Control.

Error Behavior

  • Malformed tags ({{ without }}, invalid filter names, unquoted include paths) are compile-time errors that name the template.
  • Malformed Lua inside {% ... %} blocks is caught at compile time, before any file is written.
  • In lenient mode, an undefined variable (or explicit nil) renders as an empty string — the literal text nil is never emitted.
  • A filter that raises an error fails the render with a message naming the template file.