back

How the sausage is made

2022-09-18

4 minute read

I'm writing this in the evening, I didn't write in the morning because I slept in then went to the gallery. My mind's been racing a little bit, thinking about all the things I want to do. Standard sunday.

I will write more tomorrow about my general plans, I wrote out a list in my bullet journal I'd like to transcribe.

This evening, after hanging at the gallery, I rewrote the build script for this journal. I've take out the deployment scripts that push it to neocity, I think it's best if the program only focuses on transforming markdown to html and leaves the deployment up to somebody else.

I'm calling it (for now) gopub

gopub

I think the core mission statement for this site builder is:
Bury the dead1 where they're found2

  1. that is to say:

“That for which we find words is something already dead in our hearts. There is always a kind of contempt in the act of speaking.” ― Nietzsche

  1. Sufjan Stevens

But really, I just want a site builder that treats the directory I'm in as the final product, and just does the html producing in the same/whichever directory it finds the markdown in. I know this is bad practice for a web app or a program, but I think for a journal or zine it's more true to the process. When you journal in a notebook it doesn't go through any other process to be a journal entry. I don't like to abstract more and more complexity in between the content and the form. I'd like to find a clearer way to express this idea, I don't think I have it completely resolved in my mind.

It takes two .html templates in the root directory, written as golang html templates

All markdown files are rendered with the post.html template into an index.html in whichever directory they're found in, and then an index.html is created in the root directory using the home.html template.

the <body> tag for home.html looks like this

<body>
    <main class="home">
        <h1>Louis Journal</h1>
        my <s>ass</s> heart is a pop-up-card, unfolding

        <ol reversed >
            {{range $p := .Posts}}<li><a href="{{$p.Date}}/">{{$p.Date}} - {{$p.Title}}</a></li> {{end}}
        </ol>
    </main>
</body>

and for the post.html

<body>
  <main>
    <a href="..">back</a>
    <h1>{{.Title}}</h1>
    <date>{{.Date}}</date>
    {{.Content}} 
  </main>
</body>

You can see the style.css on this website.

At the moment I just call it as gopub straight and it builds the site, but in time I'd like to style it more as a cli program, with different commands and maybe a boilerplate set up. It would also be good for it to support pages outside of dated entries.

Speaking of cli programs, I've been thinking forge.horse might be better off with some kind of repl or terminal to run the publishing and updating. Never short of ideas here are we?

At the end of this entry I'll put in the entire gopub program, it's only 130 lines, 3kb.

a terminal window with the text "✧・゚: ✧・゚: done :・゚✧:・゚✧" and "neocities push 2022-09-18" and "neocities upload index.html

listening to

"I dont like the sort of alarming, incongruous change of texture that happens when you're eating a cucumber and you get to the middle of the cucumber"

Questions

gopub source code

package main

import (
	"bytes"
	"fmt"
	"html/template"
	"io/fs"
	"log"
	"os"
	"path/filepath"

	"github.com/yuin/goldmark"
	meta "github.com/yuin/goldmark-meta"
	"github.com/yuin/goldmark/extension"
	"github.com/yuin/goldmark/parser"
	"github.com/yuin/goldmark/renderer/html"
)

func main() {
	// get our posts
	ee, err := getPosts()
	// terrible unhelpful error
	if err != nil {
		fmt.Printf("broken")
	}

	parseFiles(ee)
}

func getPosts() (posts []string, err error) {
	// walk the directory looking for posts, add them to the posts var
	err = filepath.Walk(".", func(path string, info fs.FileInfo, e error) error {
		if e != nil {
			return e
		}

		// if it's a .md file then add it to our posts
		if filepath.Ext(info.Name()) == ".md" {
			posts = append(posts, path)
			return nil
		}

		return nil
	})

	return
}

type Post struct {
	Title   string
	Date    string
	Content template.HTML
}

type Homepage struct {
	Posts []Post
}

func parseFiles(paths []string) {
	// go through the paths
	// - load em up,
	// - convert em to html,
	// - spit em out where they lie

	// set up our buffers
	var buf bytes.Buffer
	var htmlBuf bytes.Buffer
	var posts []Post

	// get our templates
	postTemplate, _ := template.ParseFiles("post.html")
	homeTemplate, _ := template.ParseFiles("home.html")

	// add our markdown extensions
	markdown := goldmark.New(
		goldmark.WithExtensions(
			meta.Meta, extension.Strikethrough,
		),
		goldmark.WithRendererOptions(
			html.WithUnsafe(),
		),
	)
	context := parser.NewContext()

	// loop through paths
	for _, path := range paths {
		// current directory
		dir := os.DirFS(".")
		// file directory
		fd := filepath.Dir(path)
		// load file
		source, _ := fs.ReadFile(dir, path)
		// parse md to html
		markdown.Convert(source, &buf, parser.WithContext(context))
		// get title from yaml head
		metaData := meta.Get(context)
		title := fmt.Sprintf("%v", metaData["title"])

		// for now, the date is the file name. Will add support for pages later
		// regexp.Match(`^\d\d\d\d-\d\d-\d\d$`, []byte(dd)); m {
		date := fd
		// }

		// create our post data to pass into the template
		post := Post{
			title,
			date,
			template.HTML((buf.Bytes())),
		}
		posts = append([]Post{post}, posts...)
		// render template
		postTemplate.Execute(&htmlBuf, post)
		// save file
		os.WriteFile(filepath.Dir(path)+"/index.html", htmlBuf.Bytes(), 0660)
		// clear buffers
		htmlBuf.Reset()
		buf.Reset()
	}

	data := Homepage{posts}
	err := homeTemplate.Execute(&htmlBuf, data)
	if err != nil {
		log.Fatal(err)
	}
	os.WriteFile("index.html", htmlBuf.Bytes(), 0660)
	// messaging
	fmt.Printf("\n✧・゚: *✧・゚:* done *:・゚✧*:・゚✧\n\n")
}

func fileNameWithoutExt(fileName string) string {
	return fileName[:len(fileName)-len(filepath.Ext(fileName))]
}