From 97fd7b60819d8edccfd951edd338c92cc0d4b9fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20=E2=80=9CAd=C3=A6dra=E2=80=9D=20Hamel?= Date: Fri, 21 Jun 2024 23:03:37 +0200 Subject: [PATCH] CSS and Images cache busting --- lib/hash.ts | 14 +++++++++++++- lib/postcss/hashes.ts | 22 ++++++++++++++++++++++ lib/tasks.ts | 2 +- lib/tasks/articles.ts | 11 +---------- lib/tasks/images.ts | 6 +++++- postcss.config.js | 8 +++++++- 6 files changed, 49 insertions(+), 14 deletions(-) create mode 100644 lib/postcss/hashes.ts diff --git a/lib/hash.ts b/lib/hash.ts index e84c63b..a698f77 100644 --- a/lib/hash.ts +++ b/lib/hash.ts @@ -1,6 +1,18 @@ import { Transform } from "node:stream"; import { createHash } from "node:crypto"; import File from "vinyl"; +import { globSync } from "glob"; +import { readFileSync } from "node:fs"; +import { PRODUCTION } from "./environment.js"; + +export function readAssetManifest(): { [entry: string]: string } { + return Object.assign( + {}, + ...globSync("dist/_assets/*.manifest").map((path) => + JSON.parse(readFileSync(path).toString()), + ), + ); +} function fileHash(buffer: Buffer) { const hash = createHash("sha256"); @@ -15,7 +27,7 @@ export default function (manifestName: string) { readableObjectMode: true, writableObjectMode: true, transform(chunk: File, _, callback) { - const hash = fileHash(chunk.contents as Buffer); + const hash = PRODUCTION ? fileHash(chunk.contents as Buffer) : "00000000"; const newName = `${chunk.basename.substring(0, chunk.basename.length - chunk.extname.length)}.${hash.substring(0, 8)}${chunk.extname}`; mappings[chunk.basename] = newName; diff --git a/lib/postcss/hashes.ts b/lib/postcss/hashes.ts new file mode 100644 index 0000000..464df10 --- /dev/null +++ b/lib/postcss/hashes.ts @@ -0,0 +1,22 @@ +import Postcss from "postcss"; +import { readAssetManifest } from "../hash.js"; + +export default (opts: any = {}): Postcss.Plugin => { + return { + postcssPlugin: "postcss-hashes", + Once() { + opts._mappings = readAssetManifest(); + }, + Declaration(decl: Postcss.Declaration) { + decl.value = decl.value.replace(/url\("([^"]+)"\)/, (v, url) => { + if (opts._mappings.hasOwnProperty(url)) { + return v.replace(url, opts._mappings[url]); + } + + return v; + }); + }, + }; +}; + +export const postcss = true; diff --git a/lib/tasks.ts b/lib/tasks.ts index ebd4a8c..4e9980b 100644 --- a/lib/tasks.ts +++ b/lib/tasks.ts @@ -7,4 +7,4 @@ import { svg } from "./tasks/svg.js"; import { watch } from "./tasks/watch.js"; export { articles, css, fonts, images, svg, watch }; -export default series(fonts, css, images, svg, articles); +export default series(fonts, images, svg, css, articles); diff --git a/lib/tasks/articles.ts b/lib/tasks/articles.ts index c26bb37..fd2fe4e 100644 --- a/lib/tasks/articles.ts +++ b/lib/tasks/articles.ts @@ -15,7 +15,7 @@ import { PRODUCTION, DEFAULT_DATE } from "../environment.js"; import rehypeExternalLinks from "rehype-external-links"; import visit from "unist-util-visit"; import { src, dest } from "gulp"; -import { globSync } from "glob"; +import { readAssetManifest } from "../hash.js"; const Asciidoctor = asciidoctor(); const EXTENSION_REGISTRY = Asciidoctor.Extensions.create(); @@ -41,15 +41,6 @@ const SITE_DEFAULT_META = { "og:locale": "en_GB", }; -function readAssetManifest(): { [entry: string]: string } { - return Object.assign( - {}, - ...globSync("dist/_assets/*.manifest").map((path) => - JSON.parse(readFileSync(path).toString()), - ), - ); -} - function extractImages(gulp: Transform, file: File) { return function () { return function (tree) { diff --git a/lib/tasks/images.ts b/lib/tasks/images.ts index f2880ac..6b23933 100644 --- a/lib/tasks/images.ts +++ b/lib/tasks/images.ts @@ -1,3 +1,7 @@ import { src, dest } from "gulp"; +import hashPaths from "../hash.js"; -export const images = () => src("src/*.avif", { encoding: false }).pipe(dest("dist/_assets/")); +export const images = () => + src("src/*.avif", { encoding: false }) + .pipe(hashPaths("images.manifest")) + .pipe(dest("dist/_assets/")); diff --git a/postcss.config.js b/postcss.config.js index e821596..5df534f 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -1,8 +1,14 @@ import importPlugin from "postcss-import"; import nestingPlugin from "postcss-nesting"; import cssnanoPlugin from "cssnano"; +import hashesPlugin from "./lib/postcss/hashes.js"; /** @type {import('postcss-load-config').Config} */ export default { - plugins: [importPlugin(), nestingPlugin(), cssnanoPlugin({ preset: "default" })], + plugins: [ + importPlugin(), + nestingPlugin(), + hashesPlugin(), + cssnanoPlugin({ preset: "default" }), + ], };