Goodmorning, this is not a tech-blog. But… idk, this might be of interest if you're on here to begin with?
Instead of steadily writing posts here the last week or so, I instead experienced what we call "executive dysfunction". The upside is that now I have a script to automatically put-together my home-page, (and one to generate my posts as a bonus).
This isn't really a tutorial, and no knowledge is assumed, I'm mainly just going to yap.
.
├── drafts
│ ├── how-this-site-is-made.org
│ └── init.org
├── index.html
├── intro.org
├── journal.org
├── posts
│ └── init.html
├── project-todo.org
├── scripts
│ ├── bb.edn
│ ├── create-homepage.bb
│ └── create-post.bb
├── style.css
└── templates
├── base.html.tmpl
└── home-page.html.tmpl
5 directories, 13 filesI'd like to point out a few different items here:
drafts: This is where all posts
originate from, all of my drafts are .org
files, but they could be any other markup language.posts: This is where drafts go when
they're ready to be exposed to the outside world (though, they need to
be converted into HTML files first, more on that in a bit).scripts: This is where my two magic
scripts live, which are the subject of this post. create-homepage.bb generates my index.html, and create-post.bb converts a draft into HTML (once
again, more on those in a bit).templates: Holds templates with HTML
skeletons, used by the scripts.intro.org, journal.org: These are both used to populate
their respective parts of my homepage.So, we have these two little template files, one for our homepage
(home-page.html.tmpl), and one for our
posts (post.html.tmpl).
Here's the latter, I'll try to explain what we're looking at:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{title}}</title>
<link href="/style.css" rel="stylesheet" type="text/css" media="all">
</head>
<body>{{content}}</body>
</html>The points of interest here are those {{title}} and {{content}} bits.
To put it briefly: {{content}} will be
replaced by an HTML-ized draft, and {{title}} will be replaced by the first header
in that draft- this is ultimately handled by create-post.bb, which we'll look at… now!
(ns create-post
(:require
[clojure.string :as str]
[selmer.parser :as selmer]
[babashka.cli :as cli]
[clojure.java.io :as io]
[babashka.process :as p :refer [shell]]))
(def templatefile "<my-project-path>/neocity/templates/post.html.tmpl")
(def cli-opts {:fname {:coerce :string}})
(defn file->title [f]
(with-open [rdr (io/reader f)]
(-> rdr (line-seq) (first) (str/split #"#|\*" 2) (last) (str/trim))))
(defn -main [{:keys [fname destdir]}]
(let [title (file->title fname)
content (:out (shell {:out :string} "pandoc" fname))]
(println
(selmer/render (slurp templatefile)
{:title title :content [:safe content]}))))
(when (= *file* (System/getProperty "babashka.file"))
(-main (cli/parse-opts *command-line-args* {:spec cli-opts})))Ok, I don't expect you to understand any of this. Just gonna give some really broad bullet-points:
templatefile variable is holding
the path of the post templatefile->title grabs the first line of
a given draft (which should also be the first header), and then strips
off the leading header-character (either "#" or "*").(:out (shell {:out :string} "pandoc" fname)),
look, I know this one is rough, just trust me when I say that this uses
the pandoc program to convert our draft
into a string of HTML.(selmer/render ...) is what actually
does the templating! It sticks our title in the {{title}}, and our content in the {{content}}.I run this script like so:
bb create-post.bb :fname [path-to-my-draft] > [path-to-our-new-post]And a new little post is born unto the world.
Next up…
Lets start with our template, it's a little more complicated than the last.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>The web site of dotdotok</title>
<link href="/style.css" rel="stylesheet" type="text/css" media="all">
</head>
<body>
{{intro}}
<h2 id="posts">Posts</h2>
<ul>
{% for post in posts %}
<li><a href={{post.path}}>{{post.title}}</a></li>
{% endfor %}
</ul>
<h2 id="friends">Friends</h2>
<ul>
{% for friend in friends %}
<li><a href={{friend.link}}>{{friend.name}}</a></li>
{% endfor %}
</ul>
<br>
<hr />
{{running-journal}}
</body>
</html>Theres 3 distinct parts to my homepage, which I'll name: "Intro", "Links", and "Running Journal".
{{intro}} will be based off of intro.org, and is the frontmatter of the site, a
brief description of who I am and my interests.journal.org.Our template is a little more interesting, and our script has to do a
little more as a result, here's the corresponding create-homepage.bb file:
(ns create-homepage
(:require
[clojure.string :as str]
[selmer.parser :as selmer]
[babashka.fs :as fs]
[babashka.cli :as cli]
[clojure.java.io :as io]
[hickory.core :as hickory]
[babashka.process :as p :refer [shell]]
[com.rpl.specter :as s :refer :all]))
(def templatefile "../templates/home-page.html.tmpl")
(defn pandoc [fname & args]
(->> args
(apply shell {:out :string} "pandoc" fname)
:out))
(def intro (pandoc "../intro.org"))
(def running-journal (pandoc "../journal.org" "--shift-heading-level-by" "1"))
(def post-list
(->> (fs/list-dir "../posts/")
(map (fn [x]
{:path x :creation-time (fs/creation-time x) :last-updated (fs/last-modified-time x)}))))
(defn file->title [f]
(->> f
hickory/parse
hickory/as-hickory
(select-one (walker #(-> % :tag (= :title))))
:content
(first)))
(def post-data
(sort-by :creation-time
(map (fn [{:keys [path creation-time last-updated]}]
{:path (-> path str (str/split #"/" 2) (last))
:title (file->title path)
:creation-time creation-time
:last-updated last-updated})
post-list)))
(spit "../index.html"
(selmer/render (slurp templatefile)
{:intro [:safe intro]
:posts post-data
:friends [{:link "https://hope42.neocities.org"
:name "hope42"}
{:link "https://iantimony.neocities.org"
:name "iantimony"}]
:running-journal [:safe running-journal]}))A few more lines in this script, but maybe some are already a little familiar.
pandoc the two files we need: intro.org and journal.org.post-list is the list of all the files
in the posts directory (along with the
date-time they were created).file->title has the same purpose as
the one from the last script, but this one has to work on files
that are already HTML, so we need to use an HTML parsing library to find
the <title> element.post-data is just post-list, but parsed out a little bit into
something more usable in our template.:friends you'll notice are hard-coded,
this is just me being lazy, it probably won't be like that
forever.spit writes immediately to ../index.htmlThis one I can just run via:
bb create-homepage.bbAnd my homepage is updated and fresh.