Skip to content

Conversation

@domoscargin
Copy link
Contributor

@domoscargin domoscargin commented Jun 7, 2025

Notes

I started by looking at metalsmith-build-cache, but that wraps the whole Metalsmith build, so you have to split the build up to make it useful, and it proved quite tricky to split ours up in a sensible way that actually saved us time.

So in the end I created a caching plugin that wraps other plugins.

It gets passed:

  • A name for the cache (like "templates" or "layouts" or "css")
  • A pattern to match ('**/*.scss')
  • A plugin to run on a cache miss
  • Some options (to set custom cache directory, or skip caching altogether if in production)

It then creates a hash from the files matching the pattern and creates a marker

/.cache/metalsmith-cache-css-[some-hash]

If that file already exists, we skip the plugin. If it doesn't, we run the plugin.

Results

It generally works great - the initial build time for npm start is about 30 - 35 seconds on my machine, with in-place and layouts being the main timesinks, taking 20+ seconds in total.

If I just cache the in-place and layouts steps, a fully cached run is about 4 seconds. It's quicker if I cache the css and javascript steps as well.

There's a small issue where the second run on identical files will still run the layouts step, because:

  • on the first run, the cache marker is generated from the pre-layouted files
  • The layout steps then run
  • on the second run, the cache marker no longer matches the post-layouted files
  • So the layout step gets run again

On subsequent runs, the layout step is skipped as expected.

To do

Better tests

I had a bunch of issues with tests on this, but I think I've mostly solved them. However, the single test I've now written makes use of fs and I suspect adding a bunch of tests and running file ops for each of them will make the suite pretty slow, so it's probably worth figuring out a decent way to mock a bunch of stuff.

Diff compiled code

We've talked about it before, but it would also be nice to have a way to diff compiled output between PRs and main, to ensure things aren't going haywire - I did this manually when coding this up, but it was a hassle.

Split the in-place and layout steps into smaller jobs

I haven't really thought about splits that would make sense, but for example, we could split these jobs into "components", "patterns", etc and have smaller jobs to run when we update files.

Or we could figure out how to only rebuild the files that have changed, via the cache wrapper's patterns option.

@domoscargin domoscargin changed the title Add caching plugin [WIP] Add caching plugin Jun 7, 2025
@netlify
Copy link

netlify bot commented Jun 7, 2025

You can preview this change here:

Name Link
🔨 Latest commit 476b8e6
🔍 Latest deploy log https://app.netlify.com/projects/govuk-design-system-preview/deploys/6846a8715dde120008bde07c
😎 Deploy Preview https://deploy-preview-4761--govuk-design-system-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants