Template Syntax
AgileBuilder's EJS rendering syntax — interpolation, conditionals, loops, the default % delimiter, the 6 built-in helpers, {{var}} file-path rendering, and how binary files are handled.
AgileBuilder renders template file contents with EJS. Whenever variables.enabled: true and a file matches filePatterns, placeholders are replaced with variable values at generation time. This page assumes the following config:
variables:
enabled: true
delimiter: "%"
Interpolation
The default delimiter is %, so tags are combinations of <% and %>:
| Syntax | Behavior |
|---|---|
<%= value %> | Outputs the value, HTML-escaped (" becomes ", etc.). |
<%- value %> | Outputs the value unescaped. |
<% ... %> | Executes JavaScript without output. |
When generating code, JSON, or YAML, values often contain quotes — prefer <%- %> so escaping does not pollute the output:
// Template
"name": "<%- appName %>"
// Generated with appName = order-service
"name": "order-service"
Change variables.delimiter to switch delimiters — with delimiter: "?" you would write <?= appName ?>.
Referencing an undefined variable fails with TEMPLATE_VARS_MISSING and suggests filling it via --var / --vars / --interactive. Template typos therefore surface immediately at generation time instead of silently producing empty strings.
Conditionals
<% if (useAuth) { %>
import { auth } from './auth';
<% } %>
Pairs best with a confirm-type question:
inquirerQuestions:
- name: useAuth
type: confirm
message: Enable authentication?
default: true
Loops
<% features.forEach(function (feature) { %>
- <%- feature %>
<% }) %>
With features set to ["auth", "logging"] this generates:
- auth
- logging
Multi-select variables (from checkbox-type questions) arrive as arrays — ideal for driving loops.
Built-in Helpers
Six naming-convention helpers are available both as top-level functions and under the helpers namespace; the two forms are equivalent:
<%= pascalCase(appName) %>
<%= helpers.kebabCase(appName) %>
With appName = "order-service":
| Helper | Call | Output |
|---|---|---|
camelCase | <%= camelCase(appName) %> | orderService |
pascalCase | <%= pascalCase(appName) %> | OrderService |
kebabCase | <%= kebabCase(camelCase(appName)) %> | order-service |
snakeCase | <%= snakeCase(pascalCase(appName)) %> | order_service |
uppercase | <%= uppercase(appName) %> | ORDER-SERVICE |
lowercase | <%= lowercase("ORDER-Service") %> | order-service |
Typical use — derive each ecosystem's naming convention from a single variable:
package.json: "name": "<%= appName %>"
Class name: export class <%= pascalCase(appName) %>Service {}
Constant: const <%= camelCase(appName) %>Version = "1.0.0";
File-Path Rendering
When variables.enabled: true, file and directory paths are rendered too. Prefer the {{variableName}} notation inside paths:
src/{{appName}}/index.ts
With appName = order-service this becomes src/order-service/index.ts.
Path rendering rules:
{{ }}supports dotted access, e.g.{{user.name}}; a missing variable renders as an empty string.- Full EJS expressions also work in paths (e.g.
<%= pascalCase(appName) %>) — paths are processed for{{ }}first, then EJS-rendered. - Path rendering depends only on
variables.enabled.filePatternscontrols whether file contents are rendered, not paths.
Files That Are Never Rendered
The following files are excluded from content rendering:
- Binary files: the CLI checks for NUL bytes near the start of a file; binaries are copied verbatim. Images, fonts, and build artifacts are safe to keep in templates.
- Config files:
.agilebuilder.config.yaml/.agilebuilder.config.jsonare never copied to the target directory. - The
.gitdirectory: skipped by default; kept only when--keep-gitis passed. - Files excluded by
filePatterns: copied as-is (see File Patterns and Hooks).
A Complete Snippet
Template file src/{{appName}}.config.ts:
export const serviceName = "<%- appName %>";
export const serviceClass = "<%- pascalCase(appName) %>Service";
<% if (useAuth) { %>
export const authEnabled = true;
<% } %>
Command and result:
ag create 1 --target ./out --var appName=order-service --var useAuth=true
// out/src/order-service.config.ts
export const serviceName = "order-service";
export const serviceClass = "OrderServiceService";
export const authEnabled = true;
What's Next
- Template Config Reference: every
variablesfield in detail. - File Patterns and Hooks: control the rendering scope and run post-generation scripts.