Skip to content

Instantly share code, notes, and snippets.

@Nooshu
Last active January 11, 2025 19:57
Show Gist options
  • Save Nooshu/edfc2e382bc249e92ab238779357c93e to your computer and use it in GitHub Desktop.
Save Nooshu/edfc2e382bc249e92ab238779357c93e to your computer and use it in GitHub Desktop.
ESM manipulation file I use on my 11ty v3.0.0 blog for manipulating my CSS.
import dotenv from "dotenv";
import CleanCSS from 'clean-css';
import fs from 'fs';
import crypto from 'crypto';
import path from 'path';
dotenv.config();
// create a single instance of the CleanCSS function
// to be used in file loops. Add additional optimisation settings in here.
const cleanCSS = new CleanCSS({
level: {
2: {
removeDuplicateRules: true // turns on removing duplicate rules
}
}
});
export function manipulateCSS(eleventyConfig) {
eleventyConfig.addShortcode("customCSS", async function(cssPath) {
// output the file with no fingerprinting if on the dev environment
// (allows auto-reload when the CSS is modified)
if (process.env.ELEVENTY_ENV === 'development') {
return `<link rel="stylesheet" href="${cssPath}">`;
}
// Using path.join for better cross-platform compatibility
const inputFile = path.join('./public', cssPath);
const outputDirectory = path.join('./_site', 'css');
const cacheDirectory = path.join('./.cache', 'css');
try {
// Check if input file exists first
if (!fs.existsSync(inputFile)) {
console.error(`Input CSS file not found: ${inputFile}`);
return '';
}
// Ensure both cache and output directories exist
for (const dir of [cacheDirectory, outputDirectory]) {
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
}
// Read the input CSS file
const inputCSS = await fs.promises.readFile(inputFile, 'utf8');
// Initialises a new hashing instance
const hash = crypto.createHash('sha256')
// Feed CSS data into the hash function
.update(inputCSS)
// Specify the hash should be returned as a hexadecimal string
.digest('hex')
// Only take the first 10 characters of the hash
.slice(0, 10);
// Generate our CSS Cache name
const cacheKey = `${hash}-${cssPath.replace(/[\/\\]/g, '-')}`;
// This is where the file will be written
const cachePath = path.join(cacheDirectory, cacheKey);
// store our manipulated CSS in this variable
let processedCSS;
// check we have a cache directory
if (fs.existsSync(cachePath)) {
// read the cached CSS file
processedCSS = await fs.promises.readFile(cachePath, 'utf8');
} else {
// Use the memoized cleanCSS instance to minify the CSS
processedCSS = cleanCSS.minify(inputCSS).styles;
await fs.promises.writeFile(cachePath, processedCSS);
}
// Split the input file path into its components (directory, filename, extension)
const parsedPath = path.parse(inputFile);
// Use path.join for output paths
const finalFilename = path.join(outputDirectory, `${parsedPath.name}-${hash}${parsedPath.ext}`);
// Write the minified CSS to the final output location with the hash in the filename
await fs.promises.writeFile(finalFilename, processedCSS);
// path manipulation for final URL
const hashedPath = finalFilename.replace(path.join('./_site'), '').replace(/\\/g, '/');
// return our final link element with minified and fingerprinted CSS.
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