A minimal static‐site generator with an emphasis on Atom feed generation.
🧸📔 Bjørn is written in Ecmascript and intended for use with Deno.
Assuming deno
is installed on your computer, you can simply do :—
./build.js ❲path-to-my-blog❳
❲path-to-my-blog❳
is optional; if you don’t supply a path, the
default is .
.
You can (of course) also run the build script using deno run
. Bjørn
requires both --allow-read
and --allow-write
functionality for
everything inside ❲path-to-my-blog❳
.
Note that 🧸📔 Bjørn will silently overwrite existing index.xhtml
files in the blog directory!!
The feed metadata file (also used for generating the blog homepage)
should be located at ❲path-to-my-blog❳/#feed.rdf
. Entry metadata
files (used for blogposts) are any files located at
❲path-to-my-blog❳/❲YYYY-MM-DD❳/❲identifier❳/#entry.rdf
. It is
recommended that you make ❲YYYY-MM-DD❳
the date of publication for
the entry. ❲identifier❳
must not contain any characters not allowed
in U·R·L’s (e·g spaces).
As the extensions of these files suggest, they are
R·D·F∕X·M·L files. These can be a little cumbersome to
write, but they are very easy to process and manipulate with tools, so
they are a forward‐thinking format. Your feed.rdf
file should
probably look something like this :—
<awol:Feed
rdf:about="https://blog.homepage.example"
xml:lang="en"
xmlns:awol="http://bblfish.net/work/atom-owl/2006-06-06/"
xmlns:dc11="http://purl.org/dc/elements/1.1/"
xmlns:foaf="http://xmlns.com/foaf/0.1/"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
>
<dc11:title>My Cool Blog</dc11:title>
<dc11:creator>
<foaf:Person rdf:about="https://author.homepage.example" foaf:name="Me"/>
</dc11:creator>
</awol:Feed>
The value of rdf:about
on the root element needs to be the absolute
U·R·L for your blog.
Your entry.rdf
will look similar, but it needs a sioc:content
property :—
<awol:Entry
xml:lang="en"
xmlns:awol="http://bblfish.net/work/atom-owl/2006-06-06/"
xmlns:dc11="http://purl.org/dc/elements/1.1/"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:sioc="http://rdfs.org/sioc/ns#"
>
<dc11:title>A Sample Post</dc11:title>
<sioc:content rdf:parseType="Markdown"><![CDATA[
Here is some *amazing* content!
I love it!
]]></sioc:content>
</awol:Entry>
In this case, the rdf:about
can be automatically filled in based on
the path to the entry from the blog root.
As you can see from the above example, 🧸📔 Bjørn supports a
(nonstandard) Markdown
value for rdf:parseType
in addition to
Literal
. Some words of caution regarding this :—
You should always put your markdown in a <![CDATA[
section.
Your Markdown must process into valid X·M·L (not merely H·T·M·L), due to 🧸📔 Bjørn not really understanding the latter.
🧸📔 Bjørn uses rusty_markdown for its Markdown processing, which is CommonMark‐compliant.
Alongside the metadata files, there are two template files,
index#feed.xhtml
(used to generate the index page) and
index#entry.xhtml
(used to generate individual blogposts). Generated
content will replace the first <bjørn-content>
element. Metadata will
be inserted into the <head>
for you automatically.
You will need a file server which supports index files with a .xhtml
file extension. A sample .rsync-filter
might be as follows :—
- /.rsync-filter
-s .git
-s .gitignore
-s /README*
-s /build.js
-s /deno.json
-s index#*.xhtml
-s #*.rdf
Feel free to add styles, additional content, and whatever else you like to the template files (as well as the rest of the website).
🧸📔 Bjørn recognizes the following hooks (defined on the global object) when it runs :—
globalThis.bjørnTransformEntryHTML(document, metadata)
:
Receives a document and a metadata object for each generated HTML
entry.
globalThis.bjørnTransformFeedAtom(document, metadata)
: Receives
a document and a metadata object for each generated Atom feed index.
globalThis.bjørnTransformFeedHTML(document, metadata)
: Receives
a document and a metadata object for each generated HTML feed index.
globalThis.bjørnTransformHead(headElement, metadata, type)
:
Receives a <head>
element, a metadata object, and a type value of
either "entry"
or "feed"
.
globalThis.bjørnTransformMetadata(metadata, type)
: Receives a
metadata object, and a type value of either "entry"
or "feed"
.
Use this to provide any initial transformations to the metadata prior
to document generation.
Naturally, when running 🧸📔 Bjørn normally from the command line, all of these hooks are undefined. You can write your own small build script to define them, and run that instead :—
#!/usr/bin/env -S deno run --allow-read --allow-write
// custom_build.js
// Define your hooks first…
globalThis.bjørnTransformHead = (headElement, metadata, type) => {
Array.from(headElement.children)
.find(($) => $.localName == "title")
.textContent += " | My Cool Site";
};
// Then run the script…
await import("./build.js");