My first post

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:

  1. Have an index.html file that has some empty divs that I can fill using javascript
  2. On the server, load this html file and use DOM manipulation to insert the markdown posts
  3. Write the posts in markdown, convert them to html, insert the html into a page then respond to the request with the generated html

Simple 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.