# 🧸📔 Bjørn

A minimal static‐site generator with an emphasis on Atom feed
generation.

## Dependencies

🧸📔 Bjørn is written in Ecmascript and intended for use with
[Deno][Deno].

## Usage

Assuming `deno` is installed on your computer, you can simply do :—

```sh
./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!!**

## Files and Locations

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][RDF11-XML] 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 :—

```xml
<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 :—

```xml
<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 :—

1. You should always put your markdown in a `<![CDATA[` section.

2. 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][rusty_markdown] for its Markdown
processing, which is [CommonMark][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.

## Serving Content

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
- /.rsync-filter
-s .git
-s .gitignore
-s /README*
-s /build.js
-s /deno.json
-s index#*.xhtml
-s #*.rdf
```

## Customization

Feel free to add styles, additional content, and whatever else you like
to the template files (as well as the rest of the website).

### Hooks

🧸📔 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 :—

```js
#!/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");
```

[CommonMark]: <https://commonmark.org>
[Deno]: <https://deno.land/>
[RDF11-XML]: <https://www.w3.org/TR/rdf-syntax-grammar/>
[rusty_markdown]: <https://deno.land/x/rusty_markdown>
