Documentation
StaPy is a Static Site Generator. It works with Python without any additional package.
A question? Contact me!
Requirements
Requires Python 3.5 or newer on any operating system.
Installation
Create a project directory anywhere and download the last release from the StaPy repository.
mkdir stapy
cd stapy
wget https://codeberg.org/magentix/stapy/archive/last.tar.gz
tar zxvf last.tar.gz --strip 1
rm last.tar.gz
HTTP server
Run standalone HTTP server:
python3 server.py
On Windows 10 just double-click on the server.py
file.
Then access the URL http://localhost:1985
Environments
Static files are generated in the web
directory. This directory contains all the necessary environment directories (devel, prod...).
For the production, add a prod
directory in the web
directory. It will contain all pages and files you need to deploy (html, css, js, images...).
After you add a new environment, you must restart the server.
Route
When a page is open in the browser, the server search a json file in source/pages
directory. The name of the json file is the same as the URL path. Examples:
URL Path | Json file |
---|---|
/ | index.html.json |
/hello.html | hello.html.json |
/hello/world.html | hello/world.html.json |
/hello/world/ | hello/world/index.html.json |
If the json file does not exist, a 404 error is sent.
Configuration
The json file contains all the data required for generate the page:
{
"title": "Page title",
"description": "Page description",
"template": "template/default.html",
"content": "content/index.html"
}
The template key is required.
Set the environment variables with the environment suffix:
{
"url.local": "http://localhost:1985/",
"url.prod": "https://www.example.com/"
}
The environment suffix must have the same name as your environment directory. For local rendering, the suffix is always "local".
A variable can have a default value:
{
"my_text": "All environments display this text",
"my_text.local": "Except the local with this"
}
Layout
A file named html.json in the source/layout
directory is used for the default html page configuration. It will be merged with the page's json file. This is useful for a global configuration.
layout/html.json
{
"title": "Default title",
"template": "template/default.html"
}
pages/index.html.json
{
"title": "Home title",
"content": "content/index.html"
}
layout/html.json + pages/index.html.json
{
"title": "Home title",
"template": "template/default.html",
"content": "content/index.html"
}
You can add layout file for any file extensions you need:
- layout/xml.json
- layout/txt.json
- ...
A common.json
config file is available for all pages (all extensions).
- layout/common.json
Finally, it is possible to add a layout file for the page subdirectories:
- layout/blog/html.json
- layout/blog/2022/html.json
JSON | Weight | Required |
---|---|---|
layout/common.json | 1 | N |
layout/html.json | 2 | N |
layout/blog/common.json | 3 | N |
layout/blog/html.json | 4 | N |
layout/blog/2022/html.json | 5 | N |
pages/blog/2022/my-first-post.html.json | 6 | Y |
Json data with higher weight will override and extend lower weights.
Tip: Add _page/
before the path to fetch json merged data.
http://localhost:1985/_page/index.html
http://localhost:1985/_page/hello.html
Template
The template file is the skeleton of the page:
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<title>{{ meta_title }}</title>
<meta name="description" content="{{ meta_description }}" />
<link rel="stylesheet" href="{{ url }}css/style.css" />
</head>
<body>
<header>
{% header %}
</header>
<main>
{% content %}
</main>
<footer>
{% footer %}
</footer>
</body>
</html>
Expressions
- All variables in double curly braces {{ }} will be replaced with the text declared in the json file for the var.
- All variables in curly brace percent {% %} will be replaced with the content of the file declared in the json file for the var.
Tip: set empty string for a content file variable in the json file to replace it with empty text.
Child block
Use specific json data for the child content with a +
separator (spaces are required):
{% post + my-first-post.html %}
The json/my-first-post.html.json
data will be accessible in the post template, with a $
before the var:
<a href="{{ url }}{{ $_path }}">{{ $post_title }}</a>
To loop json data with a query, use ~
as separator (spaces are required):
{% post ~ {key:value} {key:dir} {start:end} %}
- key:value - Search value in the data of the specified key
- key:dir - Sort by key, asc or desc
- start:end - Retrieve data from start to end
Example:
{% post ~ tags:post date:desc 1:10 %}
This query retrieves the 10 first pages with post value in tags, sorted by date. The tags and date vars must be present in the json data of the pages:
{
"date": "2022-01-01",
"tags": ["post"]
}
The json data will be accessible in the post template, with a $
before the var.
Reserved variables
In any template, the _path var is reserved for the current page path:
{{ url }}{{ _path }}
<!-- https://www.example.com/hello.html -->
The _env var contains the environment name:
<script type="text/javascript" src="{{ url }}js/init.{{ _env }}.js"></script>
<!-- https://www.example.com/js/init.prod.js -->
Resources
All necessary resources like js, css or images are copied from the source/assets
directory in all environment directories (e.g. web/prod
).
Static files
The final static HTML files and resources are added or refreshed when the pages are opened in the browser. When /hello.html
is open, the hello.html
file is automatically generated in all environment directories (e.g. web/prod
).
Plugins
A plugin allows you to add custom code when rendering the page.
There are 6 hooks:
- file_content_opened: update a file content (html, json, md...)
- page_data_merged: update the page json data if needed
- file_copy_before: do something before copying the asset file
- file_copy_after: do something after the asset file was copied
- before_content_parsed: update the template content before parsing
- after_content_parsed: update the template content after parsing
For example, to convert Markdown to HTML, add a file markdown.py
in the plugins
directory with the following content:
import markdown
import os
def file_content_opened(content: str, args: dict) -> str:
if _get_file_extension(args['path']) != 'md':
return content
return markdown.markdown(content)
def _get_file_extension(file: str) -> str:
name, extension = os.path.splitext(file)
if not extension:
extension = ''
return extension.replace('.', '')
pip3 install markdown
You can now use Markdown syntax in your content:
Hello World!
[Back to home]({{ url }})
Crawler
The website can be regenerated with a crawler. StaPy gives a python crawler script in the tools
directory.
python3 tools/crawler.py {delete} {copy} {crawl}
- delete: remove content from all environments
- copy: copy the
source/assets
directory to all the environment directories - crawl: generate all the pages declared in the
json
files
Launch crawler with no option perform delete, copy and crawl:
python3 tools/crawler.py
DELETE 200
PUT 200
HEAD 200 /index.html
HEAD 200 /hello.html
...
Deployment
If you are using an automated deployment tool like Netlify, Cloudflare Pages, Vercel or Render, configure the tool to deploy the environment directory from the git repository (e.g. web/prod
).
To deploy with Github Pages, create a docs
directory with a symlink from web/prod
to docs
. Commit and push the docs
directory, then configure Github Pages to deploy docs
.
Themes
Simple and minimal themes for StaPy: