How I Automated My Blog Workflow

tl;dr

  1. I wrote a Neovim function to create new pages automatically.
  2. It creates a new HTML page from a template and adds it to my texts.html table.
  3. I wrote another Neovim function to automatically update the edited date.

I wanted to keep my personal website completely under my own control. I did not want to depend on a static site generator to handle the structure and content for me. After creating only five pages manually, however, I started noticing how useful some automation could be. That is why I decided to build a small system of my own.

My current setup is based around a texts.html file, which contains a table listing all the texts I have written. Each entry links to a separate page stored inside the pages/ directory.

The idea is simple: since I already use Neovim to edit all my files, I wanted to create a custom command that would handle the repetitive parts of creating a new article. Running :NewText should ask me for a title, create a new HTML file, fill it with the required boilerplate, and automatically add it to my texts table.

To make this work, I first created a template HTML file. I created a new directory called templates/ and added the basic structure of a text page as text.html.

After that, I created a new blog.lua file inside my Neovim configuration directory (lua/config/) and loaded it from lua/config/lazy.lua:

require("config.blog")

The next step was creating the new_text() function. It gets the current date, asks for the title, creates the new file, replaces the placeholders in the template, and inserts a new entry into the text table.

function M.new_text()
  vim.ui.input({
    prompt = "Title: ",
  }, function(title)
    if not title or title == "" then
      return
    end

    local slug = slugify(title)
    local filename = slug .. ".html"
    local filepath = pages .. "/" .. filename
    local date = os.date("%Y-%m-%d")

    if vim.fn.filereadable(filepath) == 1 then
      print("File already exists.")
      return
    end

    local lines = vim.fn.readfile(template)

    for i, line in ipairs(lines) do
      line = line:gsub("{{TITLE}}", title)
      lines[i] = line
    end

    vim.fn.writefile(lines, filepath)

    local html = vim.fn.readfile(texts)

    local row = {
      "        <tr>",
      "          <td>",
      '            <a href="./pages/' .. filename .. '">',
      "              " .. title,
      "            </a>",
      "          </td>",
      '          <td class="shrink">' .. date .. "</td>",
      '          <td class="shrink">' .. date .. "</td>",
      "        </tr>",
    }

    for i, line in ipairs(html) do
      if line:find("<tbody>") then
        for j = #row, 1, -1 do
          table.insert(html, i + 1, row[j])
        end
        break
      end
    end

    vim.fn.writefile(html, texts)

    vim.cmd("edit " .. filepath)

    print("Created " .. filename)
  end)
end

After creating this function, I restarted Neovim and created my first page using the new command. It worked, but there was still one thing missing.

My text table stores both the creation date and the last edited date. While the creation date is added automatically when creating a page, I also wanted the edited date to update whenever I changed an existing article.

So I created another function for that:

function M.update_edited()
  local filepath = vim.fn.expand("%:p")

  if not filepath:find("/pages/") then
    return
  end

  local filename = vim.fn.expand("%:t")
  local date = os.date("%Y-%m-%d")

  local html = vim.fn.readfile(texts)

  local found = false

  for i, line in ipairs(html) do
    if line:find("./pages/" .. filename, 1, true) then
      found = true

      for j = i, math.min(i + 10, #html) do
        if html[j]:find('<td class="shrink">') then
          if html[j + 1] and html[j + 1]:find('<td class="shrink">') then
            html[j + 1] = '          <td class="shrink">' .. date .. "</td>"
            break
          end
        end
      end

      break
    end
  end

  if found then
    vim.fn.writefile(html, texts)
    print("Updated edited date")
  end
end

The function checks whether the current file is inside the pages/ directory. If it is, it searches for the matching entry in texts.html and replaces the old edited date with the current date.

The complete code can be found in my dotfiles repository if you want to take a look or suggest improvements.

That is it for now. I hope this little system continues to work well as I write more pages. If it breaks or I find a better approach, I will probably write a follow-up.

For transparency: I wrote this post myself and then used ChatGPT to correct grammatical mistakes and improve the overall reading experience, since English is not my first language. The source code, however, was almost entirely written with the help of ChatGPT.

Further reading:

  1. Which GNU/Linux Distro Is Right For Me?
  2. My Process Of Writing A Text