pine
c8d6509c71
just found a small edge case that broke metadata working on files with manually specified templates, and corrected a spelling mistake that resulted in an error |
||
---|---|---|
.gitignore | ||
LICENSE | ||
README.md | ||
settings.py | ||
vanity.py |
README.md
Vanity Press
An extensible, programmable, static site generator for Gemini first.
Features
- Extensible - Users can create their own functions and variables to use on their site
- Templates
- Inline bash commands / python commands
- Metadata, which can be used in templates or user-created functions
- Tags
- Atom feeds
Quick Start
- clone this repository
- edit
settings.py
with information like your site name, url, etc (this will be used to create atom feeds) - see
Documentation
for basic usage - run the script
vanity.py
(dochmod +x vanity.py
on Unix systems to mark it as executable, then run it with./vanity.py
. Alternatively, you could also run it withpython vanity.py
.)
Documentation
Usage
- you modify
settings.py
with configuration options ("settings.py
variables) - you apply basic metadata to desired files for use in functions and templates ("Metadata")
- you can create templates for files ("Creating Templates")
settings.py
variables
SOURCE_DIR
: the folder where you write your content inDEST_DIR
: the folder where vanity creates its output files (the location of the generated site)TEMPLATE_DIR
: where you store template filesTEMPLATE_EXTENSIONS
: an array of file extensions. Vanity will only look at files with these extensions when doing things like templating or creating dynamic content. Files with a different extension will be hard-linked instead.IGNORE_PREFIX
: folders starting with this string and all of their contents will not be added to the site. Files, however, WILL be added but won't show up in functions like {{ atom }}, {{ index }}, or {{ recent }}replace_table
: a dictionary where you can define your own variables and functions to use on your site. Furthermore, some optional values are pulled from here to generate atom feeds.
Metadata
Metadata is data appended to the start of files (metadata listed after page content begins will not be used) which provides information about the page. This info can be used by templates and functions. For example, this could be the date published, title, or tags associated with the page. Metadata names are case-insensitive and are preceded by the &
character. A :
delimiter is then used to separate it from the value. Metadata is not shown on the destination files.
For example, metadata for a simple page may look like this:
&TITLE:Vanity Press: A new static site generator for Gemini
&PUB_DATE:1970-01-01
&TAGS:Programming, Gemini
Lorem ipsum dolor asset. This is the page content
Metadata is used in a few functions:
&TITLE
is used by{{ atom }}
for page titles and{{ index }}
for link titles (defaults to the file name if not provided).&PUB_DATE
is used by{{ index }}
for specifying the page's publish date in link titles.&TAGS
is used by{{ tags }}
to organize content based on tag
Users can also use metadata in templates and define their own metadata. For example, consider the file poem.gmi
:
!TEMPLATE:text
&TITLE:Ozymandias
&AUTHOR:Percy Shelly
&POETRY:True
Lorem ipsum. Poem.
and the template text.template
:
# {{ &title }}
## by {{ &author }}
{{ python:if {{ &poetry }}:out = '\n```' }}
!CONTENT
{{ python:if {{ &poetry }}:out = '\n```' }}
which automatically wraps poems in pre-formatted text blocks and specifies their title and author.
Current functions / variables
Functions and variables are written in-line in the source directory files with the syntax {{ foo }}
or {{ foo:args }}
(foo
is the name of a function and args
are a comma separated list of arguments). The spacing between brackets does not matter, so {{ foo}}
, {{foo}}
, and {{foo }}
all work the same.
Below is a list of currently defined functions / variables that you can use. See "Extending Features" for how to create your own.
CURRENT_DIR: outputs the source directory where it's being called from
CURRENT_FILE: outputs the source file path where it's being called from
atom: outputs an atom feed for n most-recent files in the specified directory
- calling
{{ atom }}
returns an atom feed for every file in the current directory ending with an extension listed inTEMPLATE_EXTENSIONS
{{ atom:n }}
changes it to the n most-recent files{{ atom:n,dir }}
changes the directory to dir (absolute path), no matter where the function is called from
date: outputs the current date in the specified format
- calling
{{ date }}
returns the current date in the format "YYYY-mm-dd" (ISO 8601) {{ date:format }}
changes the format to format. This is a string like%Y-%m-%d
, that can be used in Python'sstrftime
function
index: outputs a list Gemini links for n most-recent files in the specified directory
- calling
{{ index }}
returns a list of Gemini links (formatted =>url
pub_date
title
) for every file in the current directory.pub_date
andtitle
will be fetched from the file's metadata if provided. Iftitle
isn't provided, it'll be replaced with the file name. {{ index:n }}
changes it to the n most-recent files{{ index:-n }}
changes it to the n oldest files.{{ index:-0 }}
will list all files without limit, oldest to newest{{ index:n,dir }}
changes the directory to dir (absolute path) no matter where the function is called from
recent: outputs a list of Gemini links for n most-recent files in the specified directory
{{ recent }}
behaves exactly like{{ index }}
does, with the exceptions that the default, unspecified count is 10 (whereas{{ index }}
is 0, no limit), and that links are formatted as =>url
date_modified
file name
last_updated: returns the modification date of the specified file in the format "YYYY-mm-dd" (ISO 8601)
{{ last_updated }}
returns the modification date of the current file
python: computes the specified python command. Outputs whatever the variable out
is defined as.
- example:
{{ python:import random; out="Hello" if random.random() < 0.5 else "World" }}
would either return "Hello" or "World" with 50/50 odds. - You can use other functions, variables, and metadata in
{{ python }}
. For example,{{ python:out="{{ &title }}".upper() }}
would output the title defined in metadata in all upper case.
shell: captures the output of the specified shell command
- IMPORTANT: Shell commands are executed in your shell, that's how they work. They are therefore inherently dangerous; calling something like
{{ shell:rm -rf {{ CURRENT_DIR }} }}
would execute and delete all of the files in the current working directory. Use with caution! - example:
{{ shell:ls | sed 's/^.*/=> & &/' }}
could be used to create a quick-and-dirty index file
tags: outputs a list of tags used (tags found via metadata) in the specified directory. In the destination directory, creates the folder tag
in the relative path
- example: calling
{{ tags }}
in./gemlog/index.gmi
would output something like:
=> tag/cooking.gmi cooking (3)
=> tag/essays.gmi essays (2)
=> tag/programming.gmi programming (5)
and create the tag index files cooking.gmi
, essays.gmi
, programming.gmi
in DEST_DIR/gemlog/tag/
. These files by default look like:
# cooking
=> /gemlog/pasta.gmi pasta.gmi
=> /gemlog/banana_bread.gmi Cooking Banana Bread at Home
=> /gemlog/vegan_chili.txt My Vegan Chili Recipe
where the titles are pulled from the metadata if available, and then default to the file name.
- You can modify the output/look of tag index files by creating the
tag.template
file in yourTEMPLATE_DIR
. See theCreating Templates
section further on for more information. - Calling
{{ tags }}
and creating the template file is usually enough to handle tags on your gemlog without any further configuration
words: outputs the word count for the current file. Takes no arguments
Creating Templates
Templates are ways for you to reuse the same content/format on multiple pages without having to manually add it every time.
Templates are files with the extension .template
stored in TEMPLATE_DIRS
. Templates can be assigned automatically or manually. For example, the template foobar.template
will automatically be applied to all files in any directories named foobar
.
Manually specifying a template must be done in the first line of a file. Writing !TEMPLATE:foobar
in the first line (followed immediately by a newline) will apply the foobar.template
template (if it exists). You can also use !TEMPLATE:none
, meaning that no template will be applied even if one would normally be used automatically.
Templates can use file metadata and defined functions / variables. Templates allow you to append prefixes / suffixes to files. For example, consider the following template:
# {{ &title }}
!CONTENT
Last updated {{ last_updated }}
Published on {{ &pub_date }}
=> / home
The title is appended before the article, sourced from the original file's metadata (&title
). At the end, a footer is applied using metadata (&pub_date
), a function (last_updated
), and just regular text (the home link). !CONTENT
is then replaced by the content of the original file.
Tag Templates
The file tag.template
, if created, modifies how tag index pages look. If not created, this default value is used:
# {{ tag }}
!CONTENT
where {{ tag }}
is replaced by the name of tag (i.e, "cooking", or "programming", etc)
Extending Features
Users can create their own variables and functions to use on their site by altering the dictionary replace_table
in settings.py
. An example is provided there to guide you further. Custom functions are written in Python in the settings.py
file. (although you could use something like the {{ shell }}
function to execute your own files).
Contributing
If you have a feature you'd like to request (or even better, to implement) I would be ecstatic to hear your input. Either make a pull request or let me know at my email, ponderosapinetree [ at ] protonmail.com. Thanks!
If you're interested in taking a peek (I encourage you to do so), the file vanity.py
is heavily marked up with comments for both your and my understanding. However, followers of any code style guide will weep at the one-liners and inconsistencies that I've used. There is no 80-character line limit, there is no God here, let ye who pass through these parts be warned.
License
All files in this repository are liscenced under CC0. Feel free (and please) change files / extend this for your use. If you do change something / add a feature that you think others may use as well, why not make a pull request, too?