lewisdale.dev/src/blog/posts/2022/10/building-a-cms-for-eleventy.md
Lewis Dale d332e34874
All checks were successful
Build and copy to prod / build-and-copy (push) Successful in 1m24s
make tag urls all lowercase, instead of having multiple urls for the same tag
2024-10-12 11:19:33 +01:00

4.8 KiB
Raw Blame History

title date slug tags
Building a CMS for Eleventy 2022-10-07T00:00:00 building-a-cms-for-eleventy
eleventy

Three days ago, I tweeted this:

https://twitter.com/LewisDaleUK/status/1577211142748807168).

I said I wouldnt be writing a CMS for Eleventy. It wasnt going to happen, theres no way. Im not in the business of reinventing the wheel.

Anyway, heres how I built a (very simple) CMS for an Eleventy site.

Why?

I wanted to build a proof-of-concept for something Id had in mind a while ago, which was a little application that could build a static web page for a local café, and allow the owners to put together new menus and have them update without any intervention from a developer.

I knew it wasnt hard to use external data sources with Eleventy - this site uses one to get book information for my reading list. What I wanted to do was seamlessly trigger that build and data retrieval.

Firstly I considered a different approach: committing files to a Git repository and pushing them. Thats fine in theory, but its very config-heavy, and relies on having an authenticated Github account attached, which isnt ideal. I want to be able to trigger the actual build.

How?

At its core, this is just an Express server with an SQLite database and the Eleventy programmatic API. I went with Express because it meant I could keep everything inside Javascript (well, Typescript), meaning I wouldnt have to execute commands from whatever platform Id written - simply, it makes it slightly easier from a package management perspective.

The flow is actually really simple. Once a user saves a menu, we trigger the Eleventy build in a separate directory. The directory contains a full Eleventy instance; this doesnt rely on the end-users configuration, as the API means I can inject what config I need and leave everything else untouched. This then builds it separately, and I can serve the files any way I want.

Issues encountered

The Eleventy Programmatic API isnt particularly well-documented, so I had to go digging through the code to work out what was going on in some spots. In particular, Id assumed that the paths I provided for output directories and config files were relative to the input path, but that proved to be false - theyre actually relative to the working directory. So while I thought I was looking for .eleventy.js in /eleventy_dir/, it was actually looking in the directory of the Express app.

This was also true for passthrough copies, which proved to be a slight issue - one of the things I didnt want to do was dictate how the Eleventy site should be configured. In the end, I found a “workaround” (read: horrible hack) that let me override the eleventyConfig.addPassthroughCopy function, and make relative paths absolute. Heres the code for it below:

new Eleventy(
	this._config.buildDir,
	this._config.outputDir,
	config: (eleventyConfig) => {
		let addPassthrough = eleventyConfig.addPassthroughCopy.bind(eleventyConfig);
		eleventyConfig.addPassthroughCopy = (file) => {
			if (typeof file === "string") {
				const filePath = {
					[path.join(this._config.rootDir || "", file)]: file
				}
				return addPassthrough(filePath);
			}
			return addPassthrough(file);
		}
		
		eleventyConfig.addGlobalData("menus", () => {
			return menus as CollectionItem[];
		});

		return {};
	}
)

Like I said, a “workaround”.

Final thoughts

So this was a fun little experiment. Its very rough-and-ready and doesnt really do a lot, but it was good to spike out how that might be done. Eagle-eyed observers of the codebase well see that theres lots of boilerplate/half-finished code for other things I was working on. Im planning on adding more features to the server, and then hopefully building an MVP of the menu application.

I think there are a few use cases for this, but mostly its a good way to build content-managed websites that are updated relatively-infrequently. I think the thing that I like about it is that it is very unprescriptive. Your specific Eleventy configuration isnt important - it adds the data it needs, and then leaves it alone (well, everything except those file paths).

The source for the Express server can be found on my Github.