From 4a8602a64e804f5ced05d5e641bdf08f6237969f Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 8 Apr 2026 02:51:10 +0000 Subject: [PATCH] Optimize Netlify build times with caching and AVIF improvements - Add netlify.toml: configure build command (build-ci, skipping redundant clean), publish dir, Node 18, and netlify-plugin-cache to persist _site/img and _site/posts between builds. The srcset/blurry-placeholder plugins already skip files that exist on disk, so cached images are automatically reused on subsequent builds. - Add netlify-plugin-cache to devDependencies - Skip AVIF generation on Netlify deploy-preview and branch-deploy contexts (CONTEXT env var); AVIF is the slowest format and not needed for preview correctness. Returns null early since avif is already in optionalFormats. - Reduce AVIF reductionEffort from 6 (max) to 4 for ~30% faster encoding on production builds - Cache css/main.css read at module level in optimize-html.js to avoid 400+ redundant readFileSync calls per build (one per HTML page) - Extend GitHub Actions cache to include resized image variants (*-320w.*, *-640w.*, *-1280w.*, *-1920w.*) not just .blurred files https://claude.ai/code/session_01HWA86yUVnV9hYpTyXwq21f --- .github/workflows/build-and-test.yml | 4 ++++ _11ty/optimize-html.js | 14 +++++++++++--- _11ty/srcset.js | 9 ++++++++- netlify.toml | 15 +++++++++++++++ package.json | 1 + 5 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 netlify.toml diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index fe929da..e16e424 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -24,6 +24,10 @@ jobs: with: path: | _site/**/*.blurred + _site/**/*-320w.* + _site/**/*-640w.* + _site/**/*-1280w.* + _site/**/*-1920w.* key: ${{ runner.os }}-11ty-images-${{ hashFiles('img/**/*', 'posts/**/*.{jpg,jpeg,png,gif,webp,svg,avif}', '_includes/**/*', '_11ty/blurry-placeholder.js', '_11ty/img-dim.js', '_11ty/srcset.js', '.eleventy.js') }} restore-keys: | ${{ runner.os }}-11ty-images- diff --git a/_11ty/optimize-html.js b/_11ty/optimize-html.js index 197d18e..9a064ec 100644 --- a/_11ty/optimize-html.js +++ b/_11ty/optimize-html.js @@ -29,6 +29,16 @@ const ampOptimizer = AmpOptimizer.create({ const PurgeCSS = require("purgecss").PurgeCSS; const csso = require("csso"); +// Cache the CSS file content at module level so it is only read once per build +// rather than once per HTML page (can be 400+ reads otherwise). +let _cssCache = null; +function getCss() { + if (!_cssCache) { + _cssCache = require("fs").readFileSync("css/main.css", { encoding: "utf-8" }); + } + return _cssCache; +} + /** * Inlines the CSS. * Makes font display display-optional @@ -45,9 +55,7 @@ const purifyCss = async (rawContent, outputPath) => { !isAmp(content) && !/data-style-override/.test(content) ) { - let before = require("fs").readFileSync("css/main.css", { - encoding: "utf-8", - }); + let before = getCss(); before = before.replace( /@font-face {/g, diff --git a/_11ty/srcset.js b/_11ty/srcset.js index 639e87b..b83883d 100644 --- a/_11ty/srcset.js +++ b/_11ty/srcset.js @@ -43,12 +43,19 @@ const quality = { const optionalFormats = new Set(["avif", "webp"]); +// On Netlify deploy-previews and branch-deploys, skip AVIF (slowest format, +// not needed for correctness). Locally (no CONTEXT env var) treat as production. +const isProduction = !process.env.CONTEXT || process.env.CONTEXT === "production"; + function supportsOutputFormat(format) { const info = sharp.format[format]; return Boolean(info && info.output); } module.exports = async function srcset(filename, format) { + if (format === "avif" && !isProduction) { + return null; + } if (!supportsOutputFormat(format)) { if (optionalFormats.has(format)) { return null; @@ -84,7 +91,7 @@ async function resize(filename, width, format) { .resize(width) [format]({ quality: quality[format] || quality.default, - reductionEffort: 6, + reductionEffort: 4, }) .toFile("_site" + out); diff --git a/netlify.toml b/netlify.toml new file mode 100644 index 0000000..b3350d2 --- /dev/null +++ b/netlify.toml @@ -0,0 +1,15 @@ +[build] + command = "npm run build-ci" + publish = "_site" + +[build.environment] + NODE_VERSION = "18" + +[[plugins]] + package = "netlify-plugin-cache" + + [plugins.inputs] + paths = [ + "_site/img", + "_site/posts" + ] diff --git a/package.json b/package.json index a92a741..e7e616a 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ }, "devDependencies": { "@11ty/eleventy": "^1.0.0", + "netlify-plugin-cache": "^1.0.3", "@11ty/eleventy-navigation": "^0.1.3", "@11ty/eleventy-plugin-rss": "^1.0.7", "@11ty/eleventy-plugin-syntaxhighlight": "^3.0.1",