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",