Server Side Generation: Why is it so infuriating?
What do I want from Server Side Generation?
Essentially, I have a logged-in version of the site, that will be different per user, and a logged-out version that is static across all users. I want the logged-out version to be pre-generated so that search engines and non-users can quickly view the site.
How hard can that be?
No, seriously. Why are the libraries so painful?
All I wanted to do was to pre-compile my calendar and series pages so that people coming in from search engines would have a better experience. I wanted to do this without having to write a bunch of new code to handle it. But after spending several days of my Christmas break trying to get it to work, I gave up on the libraries that were supposed to make it easy.
I tried integrating the libraries into the existing codebase, but all I got was weird errors. I tried generating a new codebase from a template that already included the ssg libraries, but the codebase would fail to build. Some of the libraries were ready for Vite 4, but most still required Vite 3. That didn’t stop the maintainer from forcing Vite 4 without checking to see if the repo still worked. I’ll repeat that: the “official” example codebase fails to build! WTF!
After spending a reasonable amount of time trying to get the first library to work, I gave up and started to look into a different library. This might have worked better, but I couldn’t get it to pick up the list of pages to pre-render. Finally, I started working on using the low-level Vite API, but on my way to work I realized I had another option. This new option would definitely work, but I would have to write some custom code. But I’ve written similar code before, so I figured it would be easy. It wasn’t quite as easy as I thought, but it was still pretty easy.
What I went with
I’m calling it Redneck SSG. So, there’s already a piece of code that takes in the backend data and generates all the different data files for the db site, the search site, and the feeds site. I added a new Generate SSG function to the script that will generate calendar and series pages. I then copy those files into the frontend codebase and I modified a function I found on Stack Overflow to list all those pre-generated files as part of the build.rollupOptions.input array. That gets vite to copy those files into the dist folder so Cloudflare will use them.
Yes, I had to translate the calendar and series pages into templates that could be executed by the output to db script, but that took maybe 3 hours. Not hard at all. Plus the calendar pages were already setup as a template because of previous optimizations for the calendar page. The biggest change was to hash each pre-generated result so that the frontend can know when to replace the pre-generated html with fresh html.
Technical Description
Step 1: Generate the SSG index.html files
The output2db script pulls the index-ssr.html file from the frontend codebase and fills in the title, hash, and content for each page. It then writes the files to the output folder to be copied over later. The hash is a hash of the generated content, which is generated using the same code as the feeds server.
Step 2: Loading an SSG page
When the SSG page is committed and deployed, it will then be loaded instead of the normal page. There is a little bit of Javascript that checks to see if the user is logged in (localStorage) and wipes out the pre-generated content if they are. Then the code unhides the div where the content is stored. This is the same div that was originally used for the pre-generated calendar content.
Step 3: Catching stale pages
If the user is logged in, the normal Vue template code runs, but if they aren’t…
Once the Vue app has loaded, it checks the hash of the pre-generated content against the hash of the content it pulled from the feeds server. If the hashes are different, it will replace the pre-generated content with the fresh content. If the hashes are the same, it will leave the pre-generated content in place.