jsPlumb uses Docusaurus, a super handy static site generator, for various parts of our ecosystem, including our Documentation and also our recently released stand alone components product.
While developing the site for jsPlumb Components, we wanted to include a couple of pieces of information that would vary depending on whether we were running in development mode locally (ie docusaurus start) or building for production. We looked into the various options available, all based on the dotenv plugin for Webpack, but couldn't find what we were looking for: a solution that worked, with minimal manual intervention, in both development mode and when building for production.
So we ended up building our own plugin. This plugin reads values from a JSON file and makes them available to your site via the customFields map in the Docusaurus siteConfig.
note
When I say "Docusaurus" in this post I am talking about v2. I've not used v1.
Your environment variables should be keyed under a section that identifies the environment you are targetting - "development" when using docusaurus start and "production" when running a build. The plugin reads the environment name from the environment variable NODE_ENV.
jsPlumb uses Statcounter to keep track of what pages in the documentation people are looking at. Recently, at the tail end of the work to migrate to Typescript and release version 5.x of the Community and Toolkit editions, we started using Docusaurus, a super handy static site generator that not only runs on React but has React baked right into it, allowing us to easily embed working demonstrations throughout our docs, or to embed snippets from the api documentation directly into the main documentation via React components. It's a really handy tool, and if you're looking for a static site generator I recommend giving it a look.
note
When I say "Docusaurus" in this post I am talking about v2. I've not used v1.
This is not a post about how great Docusaurus is, though. This is a quick post about a Statcounter plugin I wrote while working on the jsPlumb API documentation. Docusaurus ships with a plugin for Google Analytics, allowing to quickly add support for GA to all the pages on your site, but jsPlumb doesn't use Google Analytics. Statcounter's interface has a pleasing directness to it and gives us all the information we need. A quick look around the internets came up blank on existing Docusaurus/Statcounter integrations, so I wrote a plugin.
This is the first plugin I've written for Docusaurus and it doesn't have the most complex requirements, but I was quite impressed with the plugin mechanism. Let's start with the package.json:
Our module exports a single function that takes a context object, inside of which we can extract the siteConfig, and, from that, the themeConfig, which contains our statCounter settings.
Once we have our statCounter object, we extract the things we need from it, and complain about stuff that is missing:
const{projectId, securityCode}= statCounter;if(!projectId){thrownewError(`The statcounter plugin requires a "projectId" to be set`)}if(!securityCode){thrownewError(`The statcounter plugin requires a "securityCode" to be set`)}
Statcounter works by importing a JS file in your document's head, after setting a couple of global variables. Docusaurus makes this very easy for us - all we have to do is declare an injectHtmlTags() method in our plugin:
return{ name:'docusaurus-plugin-statcounter',getClientModules(){return[path.resolve(__dirname,'./statcounter')]},injectHtmlTags(){return{ headTags:[{ tagName:'script', innerHTML:` var sc_project="${projectId}"; var sc_invisible=1; var sc_security="${securityCode}";`},{ tagName:'script', attributes:{ src:"https://www.statcounter.com/counter/counter.js"}}]}}};
We inject two tags into the head: first we inject a script element and provide its innerHTML - this sets up the global variables. Next, we inject another script, but this time we set its src attribute, so it loads the JS from Statcounter's site. Documentation for injectHtmlTags(..) can be found here.
Given that Docusaurus is an SPA, pages get swapped in and out without new page loads, and Statcounter would be oblivious, were we not to advise it. Fortunately this was also straightforward. Note this block in our plugin's code:
This module hooks into onRouteUpdate, which I think is perhaps undocumented (for inspiration on this bit I used the Google Analytics plugin).
When a route update event occurs, we call _statcounter.record_pageview(). _statcounter is an object in the global space that was added by Statcounter's JS.
That's the whole plugin - perhaps 80 lines of code. Very straightforward - it took me about half an hour to write and deploy. Injecting HTML tags is not the only thing you can do with a Docusaurus plugin, though. Take a look through the lifecycle APIs to get a feel for what's possible.