JORDAN CAMPBELL
R&D SOFTWARE ENGINEER
cybernaut/0.1.4

This website is basic html + css. I wanted to avoid using any JS framework (even though I love using React), as I wanted to keep things simple.

The styling is via new.css with some small changes made.

Every page is just a standalone html document, but I use vercel for deployment which has some backend magic that removes the from each page in the url.

I wrote a custom template script so that I can reuse html components. The script also performs some convenience functions like swapping all the urls for local vs production builds (so that when I click links during development they don't take me to the deployed page).

Each of my HTML components are placed inside their own file. The format for a component is:

<template-NewTemplate>
  <div>
    <span>This is some template html.</span>
  </div>
</template-NewTemplate>

where NewTemplate can be any name. Note that it doesn't actually require the 'template-' prefix but I think it makes things cleaner. I can use the template by just specifying the template tag later:

<div>
  <template-NewTemplate />
  <div>
    <p>... some other HTML.</p>
  </div>
</div>

At the moment the components can't include nested components as everything is just a simple string replace, but it hasn't been a problem so far.

And yes, I know there are plenty of other template engines out there, including WebComponents, but I felt like doing it myself and it's a pretty trivial implementation.

The actual source for the build script is:

const fs = require("fs");
const path = require("path");
const { parse } = require("node-html-parser");
          
const mode = process.argv.includes("--deploy") 
              ? "production" 
              : "development";
          
// (1) Read the files in templates and construct a map of {name, data}
const templatesPath = "src/templates/";
          
const templates = fs.readdirSync(templatesPath).map((filename) => {
  const filepath = path.join(templatesPath, filename);
  const file = fs.readFileSync(filepath, { encoding: "utf-8" });
  const root = parse(file);
  return {
    name: `<${root.childNodes[0].rawTagName} />`,
    data: root.childNodes[0].childNodes.toString().substring(4).slice(0, -2),
  };
});
          
// Read the file, replace any template tags with the relevant 
// template, write back into file
const readSearchReplaceWrite = (filename) => {
  const dstFilename = path.join("public", filename.replace("src/", ""));
  let file = fs.readFileSync(filename, { encoding: "utf-8" });
  for (const { name, data } of templates) file = file.replace(name, data);
          
  if (mode == "production") {
    file = file.replaceAll(
      "https://cybernaut.dev/",
      "https://cybernaut.dev/"
    );
          
    file = file.replaceAll("", "");
  }
          
  const depth = filename.split("/").length - 2;
  file = file.replaceAll(
    '<link rel="stylesheet" href="style.css">',
    `<link rel="stylesheet" href="${"../".repeat(depth)}style.css">`
  );
          
  fs.writeFileSync(dstFilename, file);
};
          
const recurse = (filepath) => {
  const files = fs.readdirSync(filepath);
  for (const file of files) {
    const filename = path.join(filepath, file);
    const fileStat = fs.statSync(filename);
    if (fileStat.isDirectory()) {
      if (!filename.endsWith("templates")) {
        fs.mkdirSync(path.join("public", filename.replaceAll("src/", "")), {
          recursive: true,
        });
        recurse(filename);
      }
    } else if (file.endsWith("")) {
      readSearchReplaceWrite(filename);
    }
  }
};
          
fs.rmSync("public", { recursive: true, force: true });
fs.mkdirSync("public");
          
fs.copyFile("src/style.css", "public/style.css", (err) => {});
          
// (2) Read through all the files and replace any lines that contain 
// template tags with the relevant template
recurse("src");