Aww look at this post all nice and new!
I'm going to use this first post to cover how I built this lil blog on Glitch. If you haven't heard of Glitch, it is this super rad community and tool to build, remix and collaborate on web projects.
You can find this project on Glitch here
Glitch gets you started with a few simple templates. I'm really comfy using node.js
, so chose a simple express
app as my starter.
I knew I wanted to write my posts in markdown. I also decided I didn't want to use a templating language because it just feels wrong these days.
I considered using a framework like React, but that seems kind of overkill for something that should be mainly text.
After some fiddling and testing different approaches I came up with the following:
index.html
file that has some empty divs that I can fill using javascriptSimple enough.
My biggest challenge was doing DOM parsing in node. I tried using the dom-parser
library, but it wasn't working for some reason. I ended up going with my old friend Cheerio which I've used for web scraping in previous projects.
Load up the html file
const $ = cheerio.load(fs.readFileSync(__dirname + '/views/index.html'))
Grab the divs I want to add html to
const introDiv = $('#intro')
const latestPostDiv = $('#latest')
const postsDiv = $('#posts')
Read the markdown files
// Grabs the filenames of the markdown files in the directory
const posts = fs.readdirSync(__dirname + '/posts')
// This is a special markdown file for the blog intro
const intro = fs.readFileSync(__dirname + '/views/main.md', 'utf-8')
// This is really an array of modified dates of the files.
// It's used to show the most recent post on the main blog page
const stats = posts
.map(post => fs
.statSync(__dirname + '/posts/' + post).mtime)
Now I have all the ingredients to build out the blog home. When someone visits /
We convert the markdown to html using marked, populate index.html
, and serve it up!
A couple of fun challenges in this were figuring out how to display the most recent post on the homepage and generating the list of post permalinks in the navigation.
To display the most recent post I'm using the stats
array generated earlier and determining which post has the most recent modified date.
Becase posts
is an array of all the post filenames it matches the stats array (stats[0]
is the modified date for posts[0]
) I can use whatever the index is of the latest date in stats
to generate the filename/path for the most recent post.
Sounds like function time:
function getLatestPost(stats, posts) {
let latestPost;
stats.forEach((stat, i, arr) => {
if (stat > arr[i-1]) {
latestPost = __dirname + '/posts/' + posts[i]
} else {
latestPost = __dirname + '/posts/' + posts[0]
}
})
return latestPost
}
This could probaby be done with reduce
or filter
, but this works fine for now. What is whith that else statement?
If there is only one post in the folder the if statement will be false and nothing would get returned. That's no good, so we return the first item in the posts
array if all else fails.
From there it is as simple as converting the markdown file to html using marked
and inserting into the index.html
file with cheerio
latestPostDiv.html(marked(fs.readFileSync(getLatestPost(stats, posts), 'utf-8')))
To generate the list of post permalinks on the blog home was pretty straightforward. I wrote a function to convert the paths
array to a list of markdown links and then converted and inserted like above.
Where things got a little tricky was displaying the nav on a single post page. Because the path of home (https://sam-writes.glitch.me/) is different from a single post (https://sam-writes.glitch.me/posts/hello-world)
I was getting 404s because the nav looked like: https://sam-writes.glitch.me/posts/posts/hello-world
. So I wrote a quick function that changes the urls if we are not on the blog homepage.
function listPosts(posts, isHome) {
if (isHome) {
return posts
.map(post =>
`[${post.split('.')[0]}](posts/${post.split('.')[0]})`)
.join('<br />'
)
} else {
return '[Go Home](/)<br /><br />' +
posts
.map(post =>
`[${post.split('.')[0]}](${post.split('.')[0]})`)
.join('<br />')
}
}
When you visit a single post the index.html
file is reused. The only difference is removing the intro div and updating the navigation urls. I think it is kinda cool that all views are run through a single html file with no templating language.
Add a bit of css and kablamo, you've got a functional blog. I add markdown files to the posts
directly and everything else just works.