Liluat
Lightweight Lua Template engine
Install / Use
/learn @FSMaxB/LiluatREADME
liluat
Liluat is a lightweight Lua based template engine. While simple to use it's still powerfull because you can embed arbitrary Lua code in templates. It doesn't need external dependencies other than Lua itself.
Liluat is a fork of version 1.0 of slt2 by henix. Although the core concept was taken from slt2, the code has been almost completely rewritten.
Table of contents
- OS support
- Lua support
- Installation
- Basic Syntax
- Example
- API
- Trimming
- Options
- Command line utility
- Sandboxing
- Caveats
- License
- Contributing
OS support
Liluat is developed on GNU/Linux and automatically tested on GNU/Linux and Mac OS X. I have much confidence that it will also work on FreeBSD, other BSDs and on other POSIX compatible systems like e.g. Cygwin.
Windows was not tested, but it might work with some limitations:
- absolute paths in template includes won't be properly detected because they don't start with a
/ \is not supported as path separator- template files with Windows style line endings (
"\r\n") aren't supported - the unit tests for the command line won't work because they rely on a POSIX shell being available
Lua support
Liluat is automatically tested on the following Lua implementations:
- Lua 5.1
- Lua 5.2
- Lua 5.3
- LuaJIT 2.0
- LuaJIT 2.1 (beta)
Installation
Lua is available via luarocks, the following command installs it via luarocks:
# luarocks install liluat
You might need to add --local if you don't have admin (root) privileges.
Alternatively just copy the file liluat.lua to your software (this won't install the command line interface though).
Basic syntax
There are three different types of template blocks:
Code
You can write arbitrary Lua code in the form:
{{ some code }}
This allows for writing simple loops and conditions or even more complex logic.
Expressions
You can write arbitrary Lua expression that can be converted into a string like this:
{{= expression}}
Includes
You can include other template files like this:
{{include: 'templates/file_name'}}
By default the include path is either relative to the directory where the template that does the include is in or it is an absolute path starting with a /, e.g. '/tmp/template-dfjCm'. You can change this behavior using the base_path option, see Options.
Liluat is able to detect cyclic inclusion in most cases (eg. not if you used symlinks to create a cycle in the filesystem).
More
There is more to the syntax of liluat, but that will be explained later on in the section Trimming.
Example
Some basic template in action.
See example.lua:
local liluat = require("liluat")
local template = [[
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{= title}}</title>
</head>
<body>
<h1>Vegetables</h1>
<ul>
{{ -- write regular lua code in the template}}
{{for _,vegetable in ipairs(vegetables) do}}
<li><b>{{= vegetable}}</b></li>
{{end}}
</ul>
</body>
</html>
]]
-- values to render the template with
local values = {
title = "A fine selection of vegetables.",
vegetables = {
"carrot",
"cucumber",
"broccoli",
"tomato"
}
}
-- compile the template into lua code
local compiled_template = liluat.compile(template)
local rendered_template = liluat.render(compiled_template, values)
io.write(rendered_template)
Output:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>A fine selection of vegetables.</title>
</head>
<body>
<h1>Vegetables</h1>
<ul>
<li><b>carrot</b></li>
<li><b>cucumber</b></li>
<li><b>broccoli</b></li>
<li><b>tomato</b></li>
</ul>
</body>
</html>
API
liluat.compile(template, [options], [template_name], [start_path])
Compile the template into Lua code that can later be rendered. Returns a compiled template.
template: The template to compileoptions: A table containing different configuration options, see the Options section.template_name: A name to identify the template with. This is especially useful to be able to find out where a Lua syntax or runtime error is coming from.start_path: Path to start in as a working directory. If thebase_pathoption is not set, this is the path to which the first inclusion is relative to.
liluat.compile_file(filename, [options])
Same as liluat.compile but loads the template from a file. template_name is set to the filename and start_path is set to the path where the file is in. Returns a compiled template.
filename: File to load the template from.options: A table containing different configuration options, see the Options section.
liluat.render(compiled_template, [values], [options])
Render a compiled template into a string, using the given values. This runs the compiled template in a sandbox with values added to it's environment.
compiled_template: This is the output ofliluat.compile. Essentially Lua code with some meta data.values: A Lua table containing any kind of values. This can even be functions or custom data types from C. These values are accessible inside the template.options: A table containing different configuration options, see the Options section. NOTE: Most of those options only change the behavior ofliluat.compile.
liluat.render_coroutine(compiled_template, [values], [options])
Same as liluat.render but returns a function that can be run in a coroutine and will return one chunk of data at a time (so you can kind of "stream" the template rendering).
liluat.inline(template, [options], [start_path])
Load a template and return a template string where all the included templates have been inlined.
template: A template string to be inlined.options: A table containing different configuration options, see the Options section.start_path: Path to start in as a working directory. If thebase_pathoption is not set, this is the path to which the first inclusion is relative to.
liluat.get_dependencies(template, [options], [start_path])
Get a table containing all of the files that a template includes (also recursively).
template: The template to examine.options: A table containing different configuration options, see the [Options] section.start_path: Path to start in as a working directory. If thebase_pathoption is not set, this is the path to which the first inclusion is relative to.
Trimming
An important feature not yet talked about is trimming. In order to be able to write templates that look nice and readable while still keeping the output nice, some kinds of whitespaces need to be trimmed in some cases.
There are two kinds of trimming that liluat supports:
Left trimming
In case a line contains only whitespaces in front of a template block, those are removed when left trimming is enabled.
Right trimming
Right trimming, if enabled, removes newline characters directly following a template block.
Settings
The trimming can be globally enabled and disabled via the trim_left and trim_right options. Possible values are:
"all": trim all template blocks"expression": trim only expression blocks"code": trim only code blocks, this is the defaultfalse: disable trimming
Include blocks are not trimmed.
Local override
You can locally override left and right trimming via + and -, where + means, no trimming, and - means trimming. For example, the block {{+ code -}} will be trimmed right, but not left, no matter what the global trimming settings are.
Example
In this example, trim_left and trim_right are set to "code", which is the default.
{{for i = 1, 4 do}}
{{= i}}
{{end}}
{{for i = 5, 8 do}}
{{-= i-}}
{{end}}
{{for i = 9, 12 do+}}
{{-= i}}
{{end}}
Output:
1
2
3
4
5678
9
10
11
12
Options
The following options can be passed via the options table:
start_tag: Start tag to be used instead of{{end_tag: End tag to be used instead of}}trim_right: one of"all","code","expression"orfalseto disable. Default is"code". See the section Trimming for more information.trim_left: one of"all","code","expression"orfalseto disable. Default is"code". See the section Trimming for more information.base_path: Path that is used as base path for includes. Ifnilorfalse, all include paths are interpreted relative to the files path itself. Not that this doesn't influence absolute paths.reference: If set totrue,liluat.renderwill reference the environment in the sandbox instead of recursively copyiing it. This reduces part of the security of the sandbox, because values can now leak out of it. However, this option is useful if you pass in environments that use a lot of memory or contain reference cycles, see Caveats/Environment is copied.
Command line utility
Liluat comes with a command line interface:
$ runliluat --help
Usage: runliluat [options]
Options:
-h|--help
Show this message.
--values lua_table
Table containing values to use in the template.
--value-file file_name
Use a file to define the table of values to use in the template.
-t|--template-file file_name
File that contains the template
-n|--name template_name
Name to use for the template
-d|--dependencies
List the dependencies of the template (list of included files)
-i|--inline
Inline all the included files into o
Related Skills
node-connect
339.5kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
83.9kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
339.5kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
83.9kCommit, push, and open a PR
