Template Syntax#
Lokio templates use the EJS (Embedded JavaScript) format. You can insert variables, conditions, and loops directly inside template code.
Basics: Inserting Variables#
Use <%= %> to insert a value:
// Template
export class <%= name %> {}
// Input: name = "UserProfile"
// Result:
export class UserProfile {}Casing Transformations#
This is the most commonly used feature. Lokio provides functions to automatically transform variable formats.
Full Reference Table#
| Function | Input | Output |
|---|---|---|
<%= name %> | user profile | user profile (as-is) |
<%= Name %> | user profile | User profile (capitalize shortcut) |
<%= capitalize(name) %> | user profile | User profile |
<%= camelCase(name) %> | user profile | userProfile |
<%= pascalCase(name) %> | user profile | UserProfile |
<%= kebabCase(name) %> | user profile | user-profile |
<%= snakeCase(name) %> | user profile | user_profile |
<%= constantCase(name) %> | user profile | USER_PROFILE |
<%= dotCase(name) %> | user profile | user.profile |
<%= pathCase(name) %> | user profile | user/profile |
<%= titleCase(name) %> | user profile | User Profile |
<%= sentenceCase(name) %> | user profile | User profile |
<%= noSpace(name) %> | user profile | userprofile |
<%= lowercase(name) %> | User Profile | user profile |
<%= uppercase(name) %> | user profile | USER PROFILE |
<%= plural(name) %> | user | users |
<%= singular(name) %> | users | user |
Tip: Input can be in any format —
userProfile,user-profile,user_profile, orUser Profile— all will be recognized and converted correctly.
Real-World Example#
// Template (input: name = "product order")
import { Injectable } from '@nestjs/common';
// pascalCase → ProductOrderService
@Injectable()
export class <%= pascalCase(name) %>Service {
// camelCase → productOrderService
private readonly <%= camelCase(name) %>Repository;
// constantCase → PRODUCT_ORDER_TABLE
private readonly TABLE = '<%= constantCase(name) %>_TABLE';
// kebabCase → product-order (for URLs)
getRoute() {
return '/api/<%= kebabCase(name) %>';
}
}Result:
import { Injectable } from '@nestjs/common';
@Injectable()
export class ProductOrderService {
private readonly productOrderRepository;
private readonly TABLE = 'PRODUCT_ORDER_TABLE';
getRoute() {
return '/api/product-order';
}
}Conditions (if / else)#
Use <% if (...) { %> for conditional blocks.
Note:
<% %>blocks (without=) do not produce output — they are logic only.
// Template (parameter: useAsync = boolean)
export class <%= pascalCase(name) %>Service {
<% if (useAsync === 'TRUE') { %>
async findAll(): Promise<any[]> {
return [];
}
<% } else { %>
findAll(): any[] {
return [];
}
<% } %>
}Note: Boolean parameters are converted to the string
"TRUE"or"FALSE"inside the template.
Input: name = "Order", useAsync = true
Result:
export class OrderService {
async findAll(): Promise<any[]> {
return [];
}
}Conditions Based on Options#
Very useful when you have a parameter of type options (choices):
// Template (parameter: type = "http" | "grpc" | "queue")
<% if (type === 'http') { %>
import { Controller, Get } from '@nestjs/common';
@Controller('<%= kebabCase(name) %>')
export class <%= pascalCase(name) %>Controller {}
<% } else if (type === 'grpc') { %>
import { GrpcMethod } from '@nestjs/microservices';
export class <%= pascalCase(name) %>GrpcController {}
<% } else { %>
import { MessagePattern } from '@nestjs/microservices';
export class <%= pascalCase(name) %>Consumer {}
<% } %>Loops (for)#
// Template (parameter: fields = "id,name,email" — comma separated)
export interface <%= pascalCase(name) %> {
<% fields.split(',').forEach(field => { %>
<%= field.trim() %>: string;
<% }) %>
}Input: name = "User", fields = "id, name, email"
Result:
export interface User {
id: string;
name: string;
email: string;
}Dynamic Output Path#
You can also use casing transformations in the output path in configs.yaml:
templates:
- name: service
output: src/services/<%= pascalCase(name) %>Service.ts
# ↑ filename automatically in PascalCasename Input | Output path |
|---|---|
user | src/services/UserService.ts |
product order | src/services/ProductOrderService.ts |
auth-token | src/services/AuthTokenService.ts |
Template Comments#
Use <%# %> for comments — they will not appear in the output:
<%# This is a template comment, it will not be generated %>
export class <%= pascalCase(name) %> {
<%# TODO: add methods as needed %>
}Lokio Check Annotations#
Special annotations for lokio check — defines automatic lint rules:
<%# @lokio required danger "Must have constructor" constructor %>
<%# @lokio forbidden warning "Do not use console.log" console\.log %>
export class <%= pascalCase(name) %>Service {
constructor() {}
}See Advanced Features → for the full explanation of lokio check.
Tips & Tricks#
Avoiding Extra Whitespace#
EJS can produce blank lines from <% %> tags. Use - to strip whitespace:
<%- /* strip output */ %>Or arrange the template so condition blocks don't add blank lines:
export class Example {
<% if (withMethod) { %> doSomething() {}
<% } %>}Nested Transformations#
// Combining plural + pascalCase
<%= pascalCase(plural(name)) %>
// Input: "product" → "Products"
// Input: "user order" → "UserOrders"Using JS Variables in Templates#
<%
const className = pascalCase(name) + 'Service';
const routeName = kebabCase(name);
%>
export class <%= className %> {
route = '<%= routeName %>';
}