CSS and Images cache busting

This commit is contained in:
Thibault “Adædra” Hamel 2024-06-21 23:03:37 +02:00
parent f85d4d562e
commit 97fd7b6081
6 changed files with 49 additions and 14 deletions

View File

@ -1,6 +1,18 @@
import { Transform } from "node:stream"; import { Transform } from "node:stream";
import { createHash } from "node:crypto"; import { createHash } from "node:crypto";
import File from "vinyl"; 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) { function fileHash(buffer: Buffer) {
const hash = createHash("sha256"); const hash = createHash("sha256");
@ -15,7 +27,7 @@ export default function (manifestName: string) {
readableObjectMode: true, readableObjectMode: true,
writableObjectMode: true, writableObjectMode: true,
transform(chunk: File, _, callback) { 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}`; const newName = `${chunk.basename.substring(0, chunk.basename.length - chunk.extname.length)}.${hash.substring(0, 8)}${chunk.extname}`;
mappings[chunk.basename] = newName; mappings[chunk.basename] = newName;

22
lib/postcss/hashes.ts Normal file
View File

@ -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;

View File

@ -7,4 +7,4 @@ import { svg } from "./tasks/svg.js";
import { watch } from "./tasks/watch.js"; import { watch } from "./tasks/watch.js";
export { articles, css, fonts, images, svg, watch }; export { articles, css, fonts, images, svg, watch };
export default series(fonts, css, images, svg, articles); export default series(fonts, images, svg, css, articles);

View File

@ -15,7 +15,7 @@ import { PRODUCTION, DEFAULT_DATE } from "../environment.js";
import rehypeExternalLinks from "rehype-external-links"; import rehypeExternalLinks from "rehype-external-links";
import visit from "unist-util-visit"; import visit from "unist-util-visit";
import { src, dest } from "gulp"; import { src, dest } from "gulp";
import { globSync } from "glob"; import { readAssetManifest } from "../hash.js";
const Asciidoctor = asciidoctor(); const Asciidoctor = asciidoctor();
const EXTENSION_REGISTRY = Asciidoctor.Extensions.create(); const EXTENSION_REGISTRY = Asciidoctor.Extensions.create();
@ -41,15 +41,6 @@ const SITE_DEFAULT_META = {
"og:locale": "en_GB", "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) { function extractImages(gulp: Transform, file: File) {
return function () { return function () {
return function (tree) { return function (tree) {

View File

@ -1,3 +1,7 @@
import { src, dest } from "gulp"; 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/"));

View File

@ -1,8 +1,14 @@
import importPlugin from "postcss-import"; import importPlugin from "postcss-import";
import nestingPlugin from "postcss-nesting"; import nestingPlugin from "postcss-nesting";
import cssnanoPlugin from "cssnano"; import cssnanoPlugin from "cssnano";
import hashesPlugin from "./lib/postcss/hashes.js";
/** @type {import('postcss-load-config').Config} */ /** @type {import('postcss-load-config').Config} */
export default { export default {
plugins: [importPlugin(), nestingPlugin(), cssnanoPlugin({ preset: "default" })], plugins: [
importPlugin(),
nestingPlugin(),
hashesPlugin(),
cssnanoPlugin({ preset: "default" }),
],
}; };