A transparent, extensible static site generator
Extending Bagatto with Janet Libraries and jpm
.
Bagatto is designed to be transparent and extensible. These two features complement each other: by exposing as much of the functionality as we can in the form of ordinary functions and function signatures, we render the act of writing a website obvious and inspectable, and make it pratical for site authors to write their own logic or introduce libraries from the wider Janet ecosystem.
In this guide we’ll see an example of how to use jpm
to manage janet dependencies.
The library we’ll introduce is Janet Hypertext, which allows a programmer to easily generate HTML using Janet s-expressions.
Here’s some template code from the Bagatto basic-site
demo project:
{% (bagatto/include "templates/head") %}
<h1>{{ (get-in args [:config :title]) }}</h1>
<div>
<nav>
<ul>
{% (each page (args :pages)
(printf ```
<li>
<a href="%s">%s</a>
</li>
```
(page-path args page)
(page :basename))) %}
</ul>
</nav>
</div>
{% (bagatto/include "templates/foot") %}
Here’s how we could express the same template using Hypertext1:
{$ (import hypertext) $}
{% (bagatto/include "templates/head") %}
<h1>{{ (get-in args [:config :title]) }}</h1>
<div>
<nav>
<ul>
{% (each page (args :pages)
(def path (page-path args page))
(print (hypertext/markup
(li :class "dib mr2"
(a :class "pv2 ph3 fw6 nav focus-black"
:href path
[(page :basename)]))))) %}
</ul>
</nav>
</div>
{% (bagatto/include "templates/foot") %}
We make a compile-time call to (import hypertext)
(like with a normal library), and then we access the library’s functions in the template.
hypertext
is not included in Bagatto nor in the Janet standard library, so if we try to compile that template without any other changes we get an error:
code-src/bagatto/demo/basic-site [new-demos !] ⊕ bag index.janet
...
in thread <core/thread 0x7F74EC0BBF10>: error:
Encountered error rendering output for site spec index:
could not find module hypertext:
/usr/lib/janet/hypertext.temple
/usr/lib/janet/hypertext.jimage
/usr/lib/janet/hypertext.janet
/usr/lib/janet/hypertext/init.janet
/usr/lib/janet/hypertext.so
hypertext.temple
hypertext.jimage
hypertext.janet
hypertext/init.janet
hypertext.so.
Current render context:
@{:current-template "templates/index"}
By default it tries to import the hypertext library from two places:
and the library is not available.
Luckily for us, Bagatto is designed to be extensible with the rest of the Janet ecosystem.
jpm
, the Janet Package Managerjpm
allows us to manage dependencies and external libraries. There are two ways to install a library using jpm
: globally, or locally.
By default, jpm install
will install a Janet package so that any Janet runtime instance on the system can access it. We can see that in action here:
code-src/bagatto/demo/basic-site [new-demos !] ⊕ sudo jpm install https://gitlab.com/louis.jackman/janet-hypertext.git
[sudo] password for zax:
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 2), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 656 bytes | 164.00 KiB/s, done.
From https://gitlab.com/louis.jackman/janet-hypertext
* branch master -> FETCH_HEAD
ca43fef..23d5697 master -> origin/master
Updating ca43fef..23d5697
Fast-forward
README.md | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
removing /usr/lib/janet/hypertext.janet
removing manifest /usr/lib/janet/.manifests/hypertext.jdn
Uninstalled.
generating /usr/lib/janet/.manifests/hypertext.jdn...
Installed as 'hypertext'.
copying src/hypertext.janet to /usr/lib/janet...
code-src/bagatto/demo/basic-site [new-demos !] ⊕ janet
Janet 1.12.2-5df8ac5 linux/x64 - '(doc)' for help
repl:1:> (import hypertext)
nil
repl:2:> (hypertext/markup p)
"<p></p>"
repl:4:>
After running jpm
install, I can launch a normal janet
instance and import that library.
On the other hand, I generally prefer to keep my package namespaces separate by project. To do this, we simply set the JANET_PATH
environment variable. Here’s the same sequence of events, run locally:
code-src/bagatto/demo/basic-site [new-demos !] ⊕ mkdir vendor
code-src/bagatto/demo/basic-site [new-demos !] ⊕ JANET_PATH=vendor jpm install https://gitlab.com/louis.jackman/janet-hypertext.git
cloning repository https://gitlab.com/louis.jackman/janet-hypertext.git to vendor/.cache/https___gitlab.com_louis.jackman_janet-hypertext.git
Cloning into 'vendor/.cache/https___gitlab.com_louis.jackman_janet-hypertext.git'...
remote: Enumerating objects: 59, done.
remote: Counting objects: 100% (59/59), done.
remote: Compressing objects: 100% (24/24), done.
remote: Total 59 (delta 27), reused 54 (delta 25), pack-reused 0
Receiving objects: 100% (59/59), 23.68 KiB | 4.74 MiB/s, done.
Resolving deltas: 100% (27/27), done.
generating /home/zax/code-src/bagatto/demo/basic-site/vendor/.manifests/hypertext.jdn...
Installed as 'hypertext'.
copying src/hypertext.janet to /home/zax/code-src/bagatto/demo/basic-site/vendor...
code-src/bagatto/demo/basic-site [new-demos !] ⊕ JANET_PATH=vendor janet
Janet 1.12.2-5df8ac5 linux/x64 - '(doc)' for help
repl:1:> (import hypertext)
nil
repl:2:> (hypertext/markup p)
"<p></p>"
repl:3:>
By prefacing the calls to jpm
and janet
with JANET_PATH=vendor
, we can specify a dependency context that’s local to our project.
Bagatto is designed to behave like a normal Janet instance whenever possible. Therefore, we should expect the same behaviour to work with the bag
executable.
code-src/bagatto/demo/basic-site [new-demos !] ⊕ JANET_PATH=vendor/ bag index.janet
Reading config data spec...
Reading pages data spec...
Reading css data spec...
Beginning 3 jobs...
Loaded config
Loading pages...
[pages] Loading 2 files
Loading css (styles.css)...
Finished jobs.
Reading pages site spec...
Reading index site spec...
Reading css site spec...
Beginning 3 jobs...
Rendering pages...
Rendering index...
Generating path for css...
Finished jobs.
Starting worker pool with 4 workers...
[WRITE] site/index.html
[COPY] site/css/styles.css
[WRITE] site/pages/bagatto.html
[WRITE] site/pages/about.html
Terminated worker pool.
I shall leave it as an exercise to the reader to verify that the output is the same as in the demo site.
In short, you should be able to use packages with jpm
inside of Bagatto in exactly the same fashion as you do outside of it.
Bagatto is designed so that you don’t have to call janet
or jpm
when using it; however, you should be able to rely on all of your expectations and assumptions about the operation of Janet when managing your Bagatto source documents as well as leveraging external Janet code to build them.
I should establish for the record that I’m not particularly experienced with Janet Hypertext and there is probably a more elegant way to write the above. Hopefully it gives you a taste of what the library does; the purpose of this guide is to demonstrate how to use any library you might like. ↩