Created
January 22, 2025 22:56
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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