Automating Gatsby Plugin Development


Martin Koparanov

October 05, 2019

I love automating things! Automation increases my productivity and I get reproducible results. Human error also gets out of the equation once you have a nice pipeline. Great, right? Yeah, but when it comes to developing Gatsby plugins, I couldn't find any guides or solutions on how to achieve just that. I started tinkering with common tools and I think I achieved a decent result for my gatsby-theme-localization (shameless plug, sorry).

This post is NOT focused on implementation details, but more on the theoretical side of concepts. If you want to see how I implemented all of this, check out the source code of the plugin.

Testing

While developing my plugin, I wanted to refactor everything as it was quite a mess. However, I couldn't be 100% sure that everything will keep working. So, a prerequisite to start refactoring the whole codebase was to have at least some tests!

Unit Testing

The unit testing part was quite straightforward - I set up Jest with Enzyme, got it working with TypeScript and wrote some tests for my small units. There is nothing Gatsby-specific in this part.

End-to-end Testing

My project didn't have much "units" to test - most of the magic was happening in the integration with Gatsby's APIs. "How in the world do I test that?!", I kept asking myself. Then it hit me - I will just keep testing it the way I always have - I will just automate it! I have an example website for the plugin and I test everything on it. So, I just wrote automated end-to-end tests that test all the functionalities the plugin has.

Okay, but this will test just a single config of your plugin. What if you want to test a different configuration? Well, make multiple examples! Having examples is good for you plugin anyways, now you can just run tests on all of them! To be as efficient as possible, try to make the examples not overlap any functionalities.

For that part I used Cypress, it was quite easy as it is built around being simple.

Building

To build my plugin, all that is needed is just TypeScript. I just run tsc which outputs the built version - again, nothing specific to Gatsby.

Publishing

Publishing is still quite straightforward, but I needed something a little more. Usually, you publish with a single command - npm publish. However, if you do it like that, it will publish it and tag it as the latest version. I don't want that - I have beta versions of the plugin, but there is no way for the CI tool to know that I want to push a beta version.

That's why I made a small utility script that parses package.json and extracts the version and tag. It looks a bit hacky, but it does the job! (I am open to suggestions though).

const fs = require('fs');
const path = require('path');
const {version} = JSON.parse(
fs.readFileSync(path.resolve(__dirname, 'package.json'))
);
const result = version.match(/^\d*\.\d*\.\d*-(\w*)\.\d*$/);
const tag = !!result ? result[1] : 'latest';
console.log(tag);

It looks for a pattern X.X.X-tag.X and extracts the "tag" part, in my case, it was beta. If no tag is found, it just prints latest and publishes as usual.

The reason for the console.log is so that it writes to stdout so it can be used as a part of a command. You will see what I mean in the next sections.

Automating

Here comes the interesting part! For a CI/CD platform I chose CircleCI - it offers a free plan for open source projects and I don't need to host it myself - perfect for me! The only drawback for me is that you have to push your config.yaml in your repo to test if it actually works. It offers a CLI tool, but I didn't have access to my Linux machine at the time and couldn't get it to run a Docker container on Windows properly (they even say that Windows could work but is not supported).

Otherwise, if you are not too lazy (like I was) to read the documentation, it is pretty easy to configure.

Here is a quick summary of CircleCI's concepts, which are needed to understand what I am talking about: CircleCI has a concept of workflows which is basically a set of jobs to run. A job is a set of commands to run in a container. Easy enough, right?

Testing

I made a workflow for testing only which runs on every branch. You know those "All checks passed" things on Pull Requests? That's what it does. It runs the test and reports the result back.

Publishing

Before publishing, a good practice is to run tests first. I made a workflow called build_and_publish that runs tests first and if they pass, it builds the package and runs npm publish. But we don't want to publish on every branch and every commit, right? That's why this workflow runs only on commits tagged with v.*.

Remember the part about publishing with a specific tag? This is where I use it. My command looks like this: npm publish --tag $(node get-tag.js). That way, the output from the command is used as part of the command and the version is tagged correctly!

Now every time I want to release a new version I just run npm version [whatever] and push! Everything after that is automated! npm version automatically changes the version in package.json, commits the change and even tags the commit with v[the version]!

Summary

So, as it turns out, automating your development of a Gatsby plugin is not that hard! After doing all that, remember to add a fancy badge to your README so people know you mean business!

I hope you found this helpful! If you have any questions or suggestions you can hit me up on Twitter @onestopjs.


Categories: Backend, Frontend,

Tags: gatsby, react,