2022-04-25 00:22:51 +00:00
|
|
|
+++
|
|
|
|
title = "Zola"
|
|
|
|
description = "Zola is a static site generator"
|
|
|
|
date = 2025-05-01T18:00:00+00:00
|
|
|
|
updated = 2021-05-01T18:00:00+00:00
|
2022-04-25 20:24:39 +00:00
|
|
|
template = "docs/section.html"
|
2022-04-25 00:22:51 +00:00
|
|
|
sort_by = "weight"
|
|
|
|
weight = 4
|
|
|
|
draft = false
|
|
|
|
+++
|
|
|
|
|
|
|
|
# Zola
|
|
|
|
|
|
|
|
[Zola](https://getzola.org) is a static site generator written in
|
|
|
|
[Rust](../rust). It builds an entire web page out of a structured markdown
|
|
|
|
collection and a collection of HTML templates. The templating language is called
|
|
|
|
[Tera](https://tera.netlify.app).
|
|
|
|
|
|
|
|
## Running Zola
|
|
|
|
|
|
|
|
- `zola init` - Starts a new Zola project. Not for the faint of heart.
|
|
|
|
- `zola serve` - Starts a localhost webserver to serve your content for demo
|
|
|
|
- `zola build` - Builds a Zola project into a collection of static HTML.
|
|
|
|
|
|
|
|
## Project layout
|
|
|
|
|
|
|
|
The following files and folders define a basic Zola project
|
|
|
|
|
|
|
|
- `config.toml`: A configuration file in the [TOML](https://toml.io/en/)
|
|
|
|
configuration language. Can contain a lot of information that will be made
|
|
|
|
available to *all* pages in the system.
|
|
|
|
- `content/`: The structured content folder.
|
|
|
|
- `sass/`: [Sass](../sass) is a CSS preprocessor language that makes CSS easier.
|
|
|
|
- `static/`: Static content such as images, fonts, and Javascript
|
|
|
|
- `templates`: The HTML templates (and includes, etc) that define the website's
|
|
|
|
layouts
|
|
|
|
|
|
|
|
Note that if you want to use modern Javascript features such as a precompiler
|
|
|
|
like Typescript or a bundler like Webpack, that is outside the functionality of
|
|
|
|
Zola. You'll have to have a separate project for that, or a subfolder or git
|
|
|
|
submodule, and you'll have to have a separate build step for that.
|
|
|
|
|
|
|
|
It's also a bit of a shame that Tera is 100% an HTML play, and there's no HAML
|
|
|
|
or similar HTML templating involved. That would be a nifty project.
|
|
|
|
|
|
|
|
## Tera
|
|
|
|
|
|
|
|
The Tera templating language looks a lot like Django's, or for that matter any
|
|
|
|
number of templating languages. Every page generated with Tera receives a
|
|
|
|
_context_, which can be thought of as a JSON object with which to populate the
|
|
|
|
page (it is, technically, a [Serde::Serializable](https://serde.rs/)). Zola
|
|
|
|
provides a large and complex context, which is explained further down.
|
|
|
|
|
|
|
|
Tera has the following syntax. Everything within these markers is processed by
|
|
|
|
Tera; everything outside is left untouched.
|
|
|
|
|
|
|
|
- `{{` and `}}` for expressions
|
|
|
|
- `{%` and `%}` for statements
|
|
|
|
- `{#` and `#}` for comments
|
|
|
|
|
|
|
|
Statements that have a dash as part of their delimeter remove all whitespace
|
|
|
|
from around the delimiter. `{%-` says to eliminate all whitespace between this
|
|
|
|
statement and anything that came before it; `-%}` says to eliminate everything
|
|
|
|
that comes after it.
|
|
|
|
|
|
|
|
### Statements
|
|
|
|
|
2022-04-25 20:24:39 +00:00
|
|
|
#### `block`
|
|
|
|
|
|
|
|
The `block` is probably the most important statement; it defines a chunk of
|
|
|
|
content. When a template is created, it can define its whole content as a
|
|
|
|
block, and it can define subsections (note: section is a heavily overloaded
|
|
|
|
word) of itself. These blocks can be used as-is, or replaced when another
|
|
|
|
template inherits them and overwrites them.
|
|
|
|
|
|
|
|
|
|
|
|
|
2022-04-25 00:22:51 +00:00
|
|
|
#### `set`
|
|
|
|
|
|
|
|
Variables are set in a given scope with `set`:
|
|
|
|
|
|
|
|
```jinja2
|
|
|
|
{% set my_var = "hello" %}
|
|
|
|
```
|
|
|
|
|
|
|
|
Sets a variable `my_var`. Variables can contain booleans, floats, strings,
|
|
|
|
integers, and arrays. There is a special syntax, `set_global`, that can be used
|
|
|
|
to set variables one level up in scope when working inside `for` loops (i.e. in
|
|
|
|
the outer scope containing the loop, preserving its content between iterations).
|
|
|
|
|
|
|
|
#### `if`
|
|
|
|
|
|
|
|
Conditional statements are managed with `if/is ... else ... endif`
|
|
|
|
|
|
|
|
```jinja2
|
|
|
|
{% if my_var is ... %} ... {% endif %}
|
|
|
|
```
|
|
|
|
|
|
|
|
The list of `is` tests that are shipped with Zola are:
|
|
|
|
|
|
|
|
- `defined`: the given variable is defined.
|
|
|
|
- `undefined`: the given variable is undefined.
|
|
|
|
- `odd`: the given variable is an odd number.
|
|
|
|
- `even`: the given variable is an even number.
|
|
|
|
- `string`: the given variable is a string.
|
|
|
|
- `number`: the given variable is a number.
|
|
|
|
- `divisibleby`: the given expression is divisible by the arg given.
|
|
|
|
- `iterable`: Returns true if the given variable can be iterated over in Tera (i.e. is an array/tuple or an object).
|
|
|
|
- `object`: Returns true if the given variable is an object (i.e. can be iterated over key, value).
|
|
|
|
- `starting_with(string)`: Returns true if the given variable is a string and starts with the arg given.
|
|
|
|
- `ending_with(string)`: Returns true if the given variable is a string and ends with the arg given.
|
|
|
|
- `containing(val)`: Returns true if the given variable contains the arg given.
|
|
|
|
- strings: is the arg a substring?
|
2022-04-25 20:24:39 +00:00
|
|
|
- arrays: is the arg one of the members of the array?
|
|
|
|
- maps: is the arg a key of the map?
|
2022-04-25 00:22:51 +00:00
|
|
|
- `matching(regexp)`: Returns true if the given variable is a string and matches the regex in the argument.
|
|
|
|
|
|
|
|
#### `for`
|
|
|
|
|
|
|
|
Arrays are iterated with the `for x in array/map ... endfor` syntax.
|
|
|
|
|
|
|
|
```jinja2
|
|
|
|
{% for name in users %}
|
|
|
|
Hello: {{ name }}
|
|
|
|
{% endfor %}
|
|
|
|
```
|
|
|
|
|
|
|
|
Maps will provide a key/value pair. The names of the two fields are arbitrary:
|
|
|
|
|
|
|
|
```jinja2
|
|
|
|
{% for name, label in options %}
|
|
|
|
<option value="{name}">{label}</option>
|
|
|
|
{% endfor %}
|
|
|
|
```
|
|
|
|
|
|
|
|
Array filters (see below) are invoked before the loop. The following will print
|
|
|
|
the list in reverse order.
|
|
|
|
|
|
|
|
```jinja2
|
|
|
|
{% for name in users | reverse %}{{ name }}{% endfor %}
|
|
|
|
```
|
|
|
|
|
|
|
|
#### `include`
|
|
|
|
|
|
|
|
Include other content into the current template being rendered. Include strings
|
|
|
|
cannot be built out of variables, but they _may_ be a list, in which case the
|
|
|
|
first filename found is rendered. If the include block has the phrase 'ignore
|
|
|
|
missing' at the end a missing file will not cause an error at build time.
|
|
|
|
|
|
|
|
```jinja2
|
|
|
|
{% include "header.html" %}
|
|
|
|
```
|
|
|
|
|
|
|
|
#### Macros
|
|
|
|
|
|
|
|
Macros are blocks of template that can be passed variables; they're basically
|
|
|
|
just big chunks of content that can be parameterized. Macros must be defined in
|
|
|
|
a file separate from the main content and imported with a distinct syntax from
|
|
|
|
`import`:
|
|
|
|
|
|
|
|
```jinja2
|
|
|
|
{% import "header.html" as header %}
|
|
|
|
```
|
|
|
|
|
|
|
|
The header macro can be invoked in that file like this:
|
|
|
|
|
|
|
|
```jinja2
|
|
|
|
{{ header::header("My Blog!") }}
|
|
|
|
```
|
|
|
|
|
|
|
|
And an example of this macro (again, in the `header.html` file) would look like this:
|
|
|
|
|
|
|
|
```jinja2
|
|
|
|
{% macro header(title) %}
|
|
|
|
<header class="header">
|
|
|
|
<h1>{ title }</h1>
|
|
|
|
</header>
|
|
|
|
{% endmacro %}
|
|
|
|
```
|
|
|
|
|
|
|
|
|
2022-04-25 20:24:39 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
2022-04-25 00:22:51 +00:00
|
|
|
```jinja2
|
|
|
|
{% raw %}Content {{ goes here }}{% endraw %}
|
|
|
|
```
|
|
|
|
|
|
|
|
Content within the `raw` block will not be processed.
|
|
|
|
|