My friend Momoko is building a new website and she wanted some help from me to create a kind of interactive map. As part of this I thought it might be simpler and cheaper to just build the site using Gatsby JS instead of Squarespace. To test this idea out I created a test site to see how hard it would be to create a simple website with a map and some content.

Gatsby JS is pretty slick. I was genuinely surprised how easy it was to build and test a simple website. Gatsby has a pretty extensive set of plugins and it is easy to update. Some specific things I like are

  • Developing and setting up this blog was really easy
  • It was not hard for me to create a small script to publish the site S3
  • It has built in support for running as a progressive web app
  • A lot of pre-made themes
  • Support for Contentful

After finishing my short prototype for Momoko I decided to recreate my blog using Gatsby. The migration itself wasn't too hard. My site was already a static website but using a different framework. I simply had to find a theme I liked and then copied over my markdown posts. I couldn't seem to get the gatsby-plugin-s3 so I created my own script to upload files.

At some point I will update this script to handle only uploading the diff.

import S3 from 'aws-sdk/clients/s3';
import * as fs from 'fs';
import * as path from 'path';
import * as mime from 'mime-types';

const s3Client = new S3({
  apiVersion: '2006-03-01',
  region: 'us-east-1'
});

const uploadDir = (folder: string, directory: string) => {
  // get of list of files from 'dist' directory
  fs.readdir(folder, (err, files) => {
    if (!files || files.length === 0) {
      console.log(`Folder [${folder}] is empty or does not exist.`);
      return;
    }

    // for each file in the directory
    for (const fileName of files) {

      // get the full path of the file
      const filePath = path.join(folder, fileName);

      // Recursively process directories
      if (fs.lstatSync(filePath).isDirectory()) {
        const nestedDirectory = directory === '' ? fileName : `${directory}/${fileName}`;
        uploadDir(filePath, nestedDirectory);
        continue;
      }

      // read file contents
      fs.readFile(filePath, (error, fileContent) => {
        // if unable to read file contents, throw exception
        if (error) { throw error; }

        const contentType = mime.lookup(filePath);
        const key = directory === '' ? fileName : `${directory}/${fileName}`;

        // upload file to S3
        s3Client.putObject({
          Bucket: process.env.S3_BUCKET,
          Key: key,
          Body: fileContent,
          ContentType: contentType
        }, (res) => {
          console.log(`Successfully uploaded '${key}'!`);
        });
      });
    }
  });
};

// resolve full folder path
const publicFolderPath = path.join(__dirname, '../public');
uploadDir(publicFolderPath, '');