Skip to content

Instantly share code, notes, and snippets.

@polarbirke
Created May 8, 2020 12:40
Show Gist options
  • Select an option

  • Save polarbirke/d9a946ad7005a5a7e46f9e8502b1f875 to your computer and use it in GitHub Desktop.

Select an option

Save polarbirke/d9a946ad7005a5a7e46f9e8502b1f875 to your computer and use it in GitHub Desktop.
11ty (eleventy) plugin to automagically inject image height and width attributes
/**
* eleventy-plugin-imgdimensions
*
* Totally stolen from https://github.com/liamfiddler/eleventy-plugin-lazyimages
* but with a lot less features; this tiny version only injects width and height
* attributes to img tags!
*
* - NO srcset support!
* - NO lazyloading!
* - NO placeholders!
*
* @type {module:fs}
*/
const fs = require('fs');
const url = require('url');
const path = require('path');
const { JSDOM } = require('jsdom');
const Jimp = require('jimp');
const supportedExtensions = [
'jpg',
'jpeg',
'gif',
'png',
'bmp',
'tiff',
'webp',
];
const transformImgPath = (src) => {
if (src.startsWith('/') && !src.startsWith('//')) {
return `.${src}`;
}
return src;
};
const defaultImgDimensionsConfig = {
imgSelector: 'img',
transformImgPath,
cacheFile: '.imgdimensions.json',
};
let imgDimensionsConfig = defaultImgDimensionsConfig;
let imgDimensionsCache = {};
const logMessage = (message) => {
console.log(`ImgDimensions - ${message}`);
};
const loadCache = () => {
const { cacheFile } = imgDimensionsConfig;
if (!cacheFile) {
return;
}
try {
if (fs.existsSync(cacheFile)) {
const cachedData = fs.readFileSync(cacheFile, 'utf8');
imgDimensionsCache = JSON.parse(cachedData);
}
} catch(e) {
console.error('ImgDimensions: cacheFile', e);
}
};
const readCache = (imageSrc) => {
if (imageSrc in imgDimensionsCache) {
return imgDimensionsCache[imageSrc];
}
return undefined;
};
const updateCache = (imageSrc, imageData) => {
const { cacheFile } = imgDimensionsConfig;
imgDimensionsCache[imageSrc] = imageData;
if (cacheFile) {
const cacheData = JSON.stringify(imgDimensionsCache);
fs.writeFile(cacheFile, cacheData, (err) => {
if (err) {
console.error('ImgDimensions: cacheFile', e);
}
});
}
};
const getImageData = async imageSrc => {
let imageData = readCache(imageSrc);
if (imageData) {
return imageData;
}
logMessage(`started processing ${imageSrc}`);
const image = await Jimp.read(imageSrc);
const width = image.bitmap.width;
const height = image.bitmap.height;
imageData = {
width,
height,
};
logMessage(`finished processing ${imageSrc}`);
updateCache(imageSrc, imageData);
return imageData;
};
const processImage = async imgElem => {
const { transformImgPath, className } = imgDimensionsConfig;
const imgPath = transformImgPath(imgElem.src);
const fileExt = path.extname(url.parse(imgPath).pathname).substr(1);
if (!supportedExtensions.includes(fileExt.toLowerCase())) {
logMessage(`${fileExt} placeholder not supported: ${imgPath}`);
return;
}
try {
const image = await getImageData(imgPath);
imgElem.setAttribute('width', image.width);
imgElem.setAttribute('height', image.height);
} catch (e) {
console.error('ImgDimensions', imgPath, e);
}
};
const transformMarkup = async (rawContent, outputPath) => {
const { imgSelector } = imgDimensionsConfig;
let content = rawContent;
if (outputPath && outputPath.endsWith('.html')) {
const dom = new JSDOM(content);
const images = [...dom.window.document.querySelectorAll(imgSelector)];
if (images.length > 0) {
logMessage(`found ${images.length} images in ${outputPath}`);
await Promise.all(images.map(processImage));
logMessage(`processed ${images.length} images in ${outputPath}`);
content = dom.serialize();
}
}
return content;
};
module.exports = {
initArguments: {},
configFunction: (eleventyConfig, pluginOptions = {}) => {
imgDimensionsConfig = Object.assign(
{},
defaultImgDimensionsConfig,
pluginOptions
);
loadCache();
eleventyConfig.addTransform('imgdimensions', transformMarkup);
},
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment