How I made this blog
I think it is a rite of passage for any developer to write their own static site generator. So I did just that. It has only the most basic of functionalities:
- A simple templating feature (which, in retrospect, was complete overkill)
- Basic pagination (once this blog has enough posts to trigger it, anyway knocks on wood)
- Syntax highlighting in code-blocks using highlight.js
- And, of course, markdown as the actual post format
As I'm writing this I realize that I forgot about images. Oh well! The static rendering happens in four steps:
- Parcel renders the main site into the dist folder, as well as all templates
- Those templates have insertion-points for the actual content, marked by 'content§ for example)
- The static site generator goes through all posts, parses their headers, converts them to HTML using marked, and injects them into the selected template (along with metadata like publishing time)
- The generator uses special pagination templates to generate pagination pages. Currently, there is only one main pagination for all posts. I am planning to add categories at some point, but I want to try it out a little first
Problems along the way
Parcel duplicated files
I had to fight with parcel quite a lot. When parsing the templates it insisted on copying js-files and scss-files I used, so that, even though my main site and my post-templates used the same resources, they were included twice under different names and locations. I wrote my own parcel-namer plugin to avoid this. It just puts every unique file into a fixed folder. For example, all CSS goes into /css.
Additionally, since I move the posts after the fact and delete the templates, the required resources should be at a fixed location anyway. Otherwise, I can't reference them from the posts.
I probably did something wrong, but it does work with the namer plugin, so I have no reason to change it.
Here's the plugin code:
const path = require('path');
const {
Namer
} = require('@parcel/plugin');
function namingFunction(bundle) {
return `awa-${path.basename(bundle.getMainEntry()
.filePath.substring(0, bundle.getMainEntry()
.filePath.lastIndexOf('.')))}.${bundle.hashReference}`;
}
module.exports = new Namer({
name({
bundle
}) {
if (!bundle.needsStableName) {
if (bundle.type === 'css') {
return `css/${namingFunction(bundle)}.css`;
}
if (bundle.type === 'js') {
return `js/${namingFunction(bundle)}.js`;
}
}
// I currently do not care about other resources
return null;
}
});
In order to use it, it has to be an npm package which name starts with parcel-namer-
. You can use yarn link if you want to use a local plugin as I did.
Header and Footer
I decided to use web components to have the same header and footer on all pages. You have to use absolute links in those web components since Parcel does not know about links in their code.
Possible Improvements
- Writing a parcel plugin for the static-site generation. In this way, parcel would be aware of resources used in markdown.
- Images!
- Currently, all templates are parsed even though you don't need them. Since I only have one it doesn't matter. If I was to publish this generator, however, it shouldn't do that.
- Currently, all posts get re-parsed on every build. There should be a way to determine whether or not a post has changed across builds.
Conclusion
It was a nice and relaxing experience for the most part. It is fun to design one's own system instead of using something ready-made. The project scope is exactly as large as I need it (even though I overdid it with the templates, but oh well).