Lilac goes through all files in a directory, performs lexical tokenization and creates an abstract syntax tree. Lastly, it parses that tree to create the final compiled file. If a file does not contain any lilac commands, it gets skipped and a hard link is created to the original file (this also applies to images and other media) as to not create duplicate files and save on space.
Note
I mainly created this project with the intention of using it in my own future projects. When I started this, I was learning about compilers and wanted to create one of my own to understand them better.
If you are looking for a good, established static site generator, you might like Eleventy, jekyll or hugo :)
This requires you to have rust and cargo installed.
cargo install lilacAlternatively, you can clone this repo and build + install it locally.
lilac help Prints a help message.
lilac init Initiates the cwd as a lilac project.
lilac remove Removes everything lilac related in the cwd.
lilac clean Doesn't do anything at the moment.
lilac build Compiles the cwd into _lilac/build
lilac run Starts a local server with live-update.
The following commands can be inserted into files and will be compiled by lilac:
The put statement replaces itself with the contents of file.txt on compilation.
[[put path/to/file.txt]]The for statement repeats the content in the for loop for each file in the directory path/to/files. For the n-th loop iteration, the variable iterator is set to the name of the n-th file in the directory.
[[for path/to/files as iterator]]
<p>test</p>
[[put {iterator}]]
[[end]]The run statement executes a file and replaces itself with the standard output.
(shell scripts and js files (via node.js) currently supported)
[[run path/to/script.sh]]Files can be formatted in a way to create sections and subsections for partial includes. Section names are prepended by a colon and followed by the section contents. Subsections have one more colon than their immediate parent-section. A tree structure is created with subsections containing subsections that can be traversed by put statements to include only parts of a file.
Let the following the contents of file.txt:
:first_section
The sun is a mass of incandescent gas,
::first_subsection
a gigantic nuclear furnace.
::second_subsection
Where hydrogen is built into helium at a temperature of millions of degrees.
:second_section
::a
Yo-ho, it's hot, the sun is not a place where we could live.
:::a.a
But here on Earth, there'd be no life without the light it gives.
:::a.b
We need its light, we need its heat.
::b
We need its energy.
::c
Without the sun, without a doubt. There'd be no you and me.
Then [[put file.txt:first_section]] would be compiled into The sun is a mass of incandescent gas,
[[put file.txt:first_section:first_subsection]] into a gigantic nuclear furnace.
and [[put file.txt:second_section:a:a.b]] into We need its light, we need its heat.
for statements can iterate over (sub)sections instead of files in a folder.
[[for file.txt:second_section as content]]
[[put {content}]]
[[end]]would compile into
Yo-ho, it's hot, the sun is not a place where we could live.
We need its energy.
Without the sun, without a doubt. There'd be no you and me.note that file.txt:second_section:a.a and file.txt:second_section:a.b are not included because they are not immediate children of file.txt:second_section.
If you want to include the title of a section, use the ;title suffix.
[[put file.txt:second_section:a;title]] would compile into a. Not so useful unless you use it with a for statement:
[[for file.txt:second_section as content]]
[[put {content};title]]
[[end]]would compile into
a
b
cUse the ;<n> suffix to get the n-th child of a subsection.
[[put file.txt:second_section;0]] compiles into Yo-ho, it's hot, the sun is not a place where we could live.
run statements can contain parameters. [[run script.sh arg1 arg2]] will run script.sh with arg1 as the first argument and arg2 as the second argument.
put statements can contain parameters as well. [[put text.txt arg1 arg2]] will replace all occurrences of {$0} with arg1 and all occurrences of {$1} with arg2 in text.txt.
How to create a self-generating RSS-Feed with lilac:
<?xml version=1.0 encoding=UTF-8 ?>
<rss version=2.0>
<channel>
<title>my RSS feed</title>
<link>https://example.com</link>
<description>news and more</description>
<copyright>Copyright 2024, Me<\\copyright>
[[for newsfeed.txt as news]]
<item>
<title>[[put {news};title]]</title>
<link>https://example.com/news/[[put {news};title]]</link>
<description>[[put {news}:description]]</description>
<author>name@email.com</author>
</item>
[[end]]
</channel>
</rss>clap for the CLI interface
notify for listening to changes in the file structure, so I can live-reload the page
tungstenite for simple websockets
walkdir for traversing directories
and the usual suspects: serde, toml and regex
