Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@tmcw
Created April 19, 2020 18:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save tmcw/d891a9cc531678b74e483818b5156ed1 to your computer and use it in GitHub Desktop.
Save tmcw/d891a9cc531678b74e483818b5156ed1 to your computer and use it in GitHub Desktop.
const glob = require("glob");
const fs = require("fs");
const path = require("path");
const { PurgeCSS } = require("purgecss");
const cssnano = require("cssnano");
const cheerio = require("cheerio");
const minify = require("html-minifier").minify;
const sharp = require("sharp");
// This runs on Netlify after Jekyll builds a full site.
// It implements two main optimizations:
// - Removes unused CSS classes
// - Minifies HTML
async function processImage($, im) {
const img = $(im);
const src = img.attr("src");
if (!src.startsWith("/images/")) return;
const ext = path.extname(src);
if (ext !== ".jpg") return;
const input = sharp(path.join("_site/", src));
const resizeTo = 768;
const webpSrc = src.replace(ext, `.webp`);
await Promise.all([input.webp().toFile(path.join("_site/", webpSrc))]);
const $picture = cheerio.load(`<picture>
<source srcset="${webpSrc}" type="image/webp" />
</picture>`);
$picture("picture").append(img.parent().html());
return $picture("body").html();
}
glob.sync("_site/**/*.html").map(async (file, i, files) => {
if (file.includes("googleb4d9bcf58c690c60")) return;
const htmlSource = fs.readFileSync(file, "utf8");
const $ = cheerio.load(htmlSource);
const styles = $("style");
const css = styles
.map(function() {
return $(this).html();
})
.get()
.join("\n");
styles.each(function(index) {
if (index > 0) {
$(this).remove();
} else {
$(this).html("");
}
});
const imgs = $(".body > p > img").get();
await Promise.all(
imgs.map(async (im) => {
const replacement = await processImage($, im);
if (replacement) {
$(im).replaceWith(replacement);
}
})
);
const [{ css: purified }] = await new PurgeCSS().purge({
content: [
{
raw: $.html(),
extension: "html",
},
],
css: [
{
raw: css,
},
],
});
const minified = (await cssnano.process(purified)).css;
$(styles.get(0)).html(minified);
const output = minify($.html(), {
collapseWhitespace: true,
removeAttributeQuotes: true,
sortAttributes: true,
collapseBooleanAttributes: true,
decodeEntities: true,
});
console.log(
`${`${i + 1}/${files.length}`.padStart(
String(files.length).length * 2 + 1,
" "
)} ${file}`
);
await new Promise((resolve) => fs.writeFile(file, output, resolve));
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment