Skip to content

Instantly share code, notes, and snippets.

@Nooshu
Created January 22, 2025 22:56
Show Gist options
  • Save Nooshu/e8a4b27496cd077f3ab7836e57705142 to your computer and use it in GitHub Desktop.
Save Nooshu/e8a4b27496cd077f3ab7836e57705142 to your computer and use it in GitHub Desktop.
CSS manipulation version with Brotli 11 compression, file fingerprinting and renaming back to the .css extension for use with Eleventy v3.0.0.
import zlib from 'zlib';
import dotenv from "dotenv";
import CleanCSS from 'clean-css';
import fs from 'fs';
import crypto from 'crypto';
import path from 'path';
dotenv.config();
// An example of how you could add additional CleanCSS settings if required
const cleanCSS = new CleanCSS({
level: {
2: {
removeDuplicateRules: true
}
}
});
// Default Brotli compression level if not set in the environment
const DEFAULT_BROTLI_COMPRESSION_LEVEL = 6;
export function manipulateCSS(eleventyConfig) {
eleventyConfig.addShortcode("customCSS", async function(cssPath) {
if (process.env.ELEVENTY_ENV === 'development') {
return `<link rel="stylesheet" href="${cssPath}">`;
}
const inputFile = path.join('./public', cssPath);
const outputDirectory = path.join('./_site', 'css');
const cacheDirectory = path.join('./.cache', 'css');
// Get compression level from the environment or use the default
const brotliCompressionLevel = parseInt(process.env.BROTLI_COMPRESSION_LEVEL || DEFAULT_BROTLI_COMPRESSION_LEVEL, 10);
try {
if (!fs.existsSync(inputFile)) {
console.error(`Input CSS file not found: ${inputFile}`);
return '';
}
for (const dir of [cacheDirectory, outputDirectory]) {
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
}
const inputCSS = await fs.promises.readFile(inputFile, 'utf8');
const hash = crypto.createHash('sha256').update(inputCSS).digest('hex').slice(0, 10);
const cacheKey = `${hash}-${cssPath.replace(/[\/\\]/g, '-')}`;
const cachePath = path.join(cacheDirectory, cacheKey);
let processedCSS;
if (fs.existsSync(cachePath)) {
processedCSS = await fs.promises.readFile(cachePath, 'utf8');
} else {
processedCSS = cleanCSS.minify(inputCSS).styles;
await fs.promises.writeFile(cachePath, processedCSS);
}
const parsedPath = path.parse(inputFile);
const finalFilename = path.join(outputDirectory, `${parsedPath.name}-${hash}${parsedPath.ext}`);
await fs.promises.writeFile(finalFilename, processedCSS);
// Brotli compression with renaming
const compressedFilename = path.join(outputDirectory, `${parsedPath.name}-${hash}-compressed${parsedPath.ext}`);
// Only compress to Brotli if the file doesn't exist
if (!fs.existsSync(compressedFilename)) {
// Set our zlib options here e.g. compression
const brotliOptions = {
level: brotliCompressionLevel
};
// zlib does it's compression magic!
const brotliBuffer = zlib.brotliCompressSync(Buffer.from(processedCSS), brotliOptions);
// Write the compressed code to the output filename defined above
await fs.promises.writeFile(compressedFilename, brotliBuffer);
}
const hashedPath = compressedFilename.replace(path.join('./_site'), '').replace(/\\/g, '/');
return `<link rel="stylesheet" href="${hashedPath}">`;
} catch (err) {
console.error("Error processing CSS:", err);
return "";
}
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment