Revert "Introduce a new "Old Image Formats" rule"

This commit is contained in:
Gaël Métais
2023-08-12 04:07:03 +02:00
committed by GitHub
parent 599d02ea0e
commit 773249ca4f
14 changed files with 164 additions and 396 deletions
+54 -23
View File
@@ -1,12 +1,20 @@
var debug = require('debug')('ylt:contentTypeChecker');
var Q = require('q');
var FileType = require('file-type');
var isSvg = require('is-svg');
var isJson = require('is-json');
var debug = require('debug')('ylt:contentTypeChecker');
var Q = require('q');
var isJpg = require('is-jpg');
var isPng = require('is-png');
var isSvg = require('is-svg');
var isGif = require('is-gif');
var isWebp = require('is-webp');
var isWoff = require('is-woff');
var isWoff2 = require('is-woff2');
var isOtf = require('is-otf');
var isTtf = require('is-ttf');
var isEot = require('is-eot');
var isJson = require('is-json');
var ContentTypeChecker = function() {
async function checkContentType(entry) {
function checkContentType(entry) {
var deferred = Q.defer();
// Setting isSomething values:
@@ -47,12 +55,12 @@ var ContentTypeChecker = function() {
var foundType;
try {
foundType = await findContentType(entry.weightCheck.bodyBuffer);
foundType = findContentType(entry.weightCheck.bodyBuffer);
// If it's an image or a font, then rewrite.
if (foundType !== null && (foundType.type === 'image' || foundType.type === 'webfont' || foundType.type === 'json')) {
if (foundType.type !== entry.type) {
debug('Content type %s is wrong for %s. It should be %s.', entry.type, entry.url, foundType.type);
debug('Content type %s is wrong for %s. It should be %s.', entry.type, entry.ulr, foundType.type);
}
rewriteContentType(entry, foundType);
}
@@ -68,21 +76,52 @@ var ContentTypeChecker = function() {
return deferred.promise;
}
async function findContentType(bodyBuffer) {
function findContentType(bodyBuffer) {
var bodyStr = bodyBuffer.toString();
if (isJpg(bodyBuffer)) {
return contentTypes.jpeg;
}
if (isPng(bodyBuffer)) {
return contentTypes.png;
}
// https://github.com/sindresorhus/is-svg/issues/7
if (/<svg/.test(bodyStr) && isSvg(bodyStr)) {
return contentTypes.svg;
}
if (isJson(bodyStr)) {
return contentTypes.json;
if (isGif(bodyBuffer)) {
return contentTypes.gif;
}
const type = await FileType.fromBuffer(bodyBuffer);
if (type && type.ext && contentTypes[type.ext]) {
return contentTypes[type.ext];
if (isWebp(bodyBuffer)) {
return contentTypes.webp;
}
if (isWoff(bodyBuffer)) {
return contentTypes.woff;
}
if (isWoff2(bodyBuffer)) {
return contentTypes.woff2;
}
if (isOtf(bodyBuffer)) {
return contentTypes.otf;
}
if (isTtf(bodyBuffer)) {
return contentTypes.ttf;
}
if (isEot(bodyBuffer)) {
return contentTypes.eot;
}
if (isJson(bodyStr)) {
return contentTypes.json;
}
return null;
@@ -107,7 +146,7 @@ var ContentTypeChecker = function() {
}
var contentTypes = {
jpg: {
jpeg: {
type: 'image',
mimes: ['image/jpeg'],
updateFn: function(entry) {
@@ -148,14 +187,6 @@ var ContentTypeChecker = function() {
entry.isImage = true;
}
},
avif: {
type: 'image',
mimes: ['image/avif'],
updateFn: function(entry) {
entry.type = 'image';
entry.isImage = true;
}
},
woff: {
type: 'webfont',
mimes: ['application/x-font-woff', 'application/font-woff', 'font/woff'],
-4
View File
@@ -43,10 +43,6 @@ var ImageDimensions = function() {
return entry.isImage && entry.contentType === 'image/png';
}
function isWebP(entry) {
return entry.isImage && entry.contentType === 'image/webp';
}
return {
getDimensions: getDimensions
};
-138
View File
@@ -1,138 +0,0 @@
var debug = require('debug')('ylt:imageReformater');
var sharp = require('sharp');
// Disable sharp cache to reduce the "disk is full" error on Amazon Lambda
sharp.cache(false);
var ImageOptimizer = function() {
// https://www.industrialempathy.com/posts/avif-webp-quality-settings
const WEBP_QUALITY = 82;
const AVIF_QUALITY = 64;
async function reformatImage(entry) {
if (!entry.weightCheck || !entry.weightCheck.bodyBuffer) {
// No valid file available
return entry;
}
var fileSize = entry.weightCheck.uncompressedSize;
debug('Let\'s try to convert %s to other image formats', entry.url);
debug('Current file size is %d', fileSize);
var animated = await isAnimated(entry);
debug('Check if the file is animated: %s', animated);
if (isJPEG(entry) || isPNG(entry)) {
debug('File is %s, let\'s try to convert it to WebP', entry.contentType);
try {
const webpFile = await convertToWebp(entry.weightCheck.bodyBuffer, animated);
if (webpFile) {
var webpFileSize = webpFile.length;
debug('WebP transformation complete for %s', entry.url);
debug('WebP size is %d bytes', webpFileSize);
if (webpFile.length > 0 && gainIsEnough(fileSize, webpFileSize)) {
entry.weightCheck.webpSize = webpFileSize;
debug('WebP size is %d bytes smaller (-%d%)', fileSize - webpFileSize, Math.round((fileSize - webpFileSize) * 100 / fileSize));
}
} else {
debug('Convertion to WebP didn\'t work');
}
} catch(err) {
debug('Error while converting to WebP, ignoring');
}
}
if (!animated && (isJPEG(entry) || isPNG(entry) || isWebP(entry))) {
debug('File is %s and is not animated, let\'s try to convert it to AVIF', entry.contentType);
try {
const avifFile = await convertToAvif(entry.weightCheck.bodyBuffer);
if (avifFile) {
var avifFileSize = avifFile.length;
debug('AVIF transformation complete for %s', entry.url);
debug('AVIF size is %d bytes', avifFileSize);
if (avifFile.length > 0 && gainIsEnough(fileSize, avifFileSize)) {
entry.weightCheck.avifSize = avifFileSize;
debug('AVIF size is %d bytes smaller (-%d%)', fileSize - avifFileSize, Math.round((fileSize - avifFileSize) * 100 / fileSize));
}
} else {
debug('Convertion to AVIF didn\'t work');
}
} catch(err) {
debug('Error while converting to AVIF, ignoring');
}
}
return entry;
}
async function convertToWebp(bodyBuffer, isAnimated) {
return sharp(bodyBuffer, {animated: isAnimated})
.webp({quality: WEBP_QUALITY, alphaQuality: WEBP_QUALITY})
.toBuffer();
}
async function convertToAvif(bodyBuffer) {
return sharp(bodyBuffer)
.webp({quality: AVIF_QUALITY})
.toBuffer();
}
// The gain is estimated of enough value if it's over 2KB or over 20%,
// but it's ignored if is below 100 bytes
function gainIsEnough(oldWeight, newWeight) {
var gain = oldWeight - newWeight;
var ratio = gain / oldWeight;
return (gain > 2048 || (ratio > 0.2 && gain > 100));
}
function isJPEG(entry) {
return entry.isImage && entry.contentType === 'image/jpeg';
}
function isPNG(entry) {
return entry.isImage && entry.contentType === 'image/png';
}
function isWebP(entry) {
return entry.isImage && entry.contentType === 'image/webp';
}
function entryTypeCanBeReformated(entry) {
return isJPEG(entry) || isPNG(entry) || isWebP(entry);
}
async function isAnimated(entry) {
if (isWebP(entry)) {
const metadata = await sharp(entry.weightCheck.bodyBuffer).metadata();
return metadata.pages > 1;
}
return false;
}
return {
reformatImage: reformatImage,
convertToWebp: convertToWebp,
convertToAvif: convertToAvif,
gainIsEnough: gainIsEnough,
entryTypeCanBeReformated: entryTypeCanBeReformated,
isAnimated: isAnimated
};
};
module.exports = new ImageOptimizer();
+3 -73
View File
@@ -15,7 +15,6 @@ var request = require('request');
var md5 = require('md5');
var imageOptimizer = require('./imageOptimizer');
var imageReformater = require('./imageReformater');
var fileMinifier = require('./fileMinifier');
var gzipCompressor = require('./gzipCompressor');
var brotliCompressor = require('./brotliCompressor');
@@ -79,10 +78,6 @@ var Redownload = function() {
.then(imageOptimizer.optimizeImage)
.then(function(entry) {
return Q(imageReformater.reformatImage(entry));
})
.then(imageDimensions.getDimensions)
.then(fileMinifier.minifyFile)
@@ -96,7 +91,7 @@ var Redownload = function() {
})
.then(function(newEntry) {
debug('File %s - Redownloaded, optimized, reformated, minified, compressed, analyzed: done', entry.url);
debug('File %s - Redownloaded, optimized, minified, compressed, analyzed: done', entry.url);
// For the progress bar
doneCount ++;
@@ -159,10 +154,6 @@ var Redownload = function() {
offenders.imageOptimization = listImagesNotOptimized(results);
metrics.imageOptimization = offenders.imageOptimization.totalGain;
// Old image formats
offenders.oldImageFormats = listImagesWithOldFormats(results);
metrics.oldImageFormats = offenders.oldImageFormats.totalGain;
// Image width
offenders.imagesTooLarge = listImagesTooLarge(results, data.params.options.device);
metrics.imagesTooLarge = offenders.imagesTooLarge.length;
@@ -407,67 +398,6 @@ var Redownload = function() {
return results;
}
function listImagesWithOldFormats(requests) {
var results = {
totalGain: 0,
images: []
};
requests.forEach(function(req) {
if (req.weightCheck.bodySize > 0 &&
imageReformater.entryTypeCanBeReformated(req) &&
(req.weightCheck.webpSize > 0 || req.weightCheck.avifSize > 0)) {
var image = {
url: req.url,
originalWeigth: req.weightCheck.bodySize,
};
switch (req.contentType) {
case 'image/jpeg':
image.originalFormat = 'JPEG';
break;
case 'image/png':
image.originalFormat = 'PNG';
break;
case 'image/gif':
image.originalFormat = 'GIF';
break;
case 'image/webp':
image.originalFormat = 'WebP';
break;
case 'image/avif':
image.originalFormat = 'AVIF';
break;
}
if (req.weightCheck.webpSize) {
image.webpSize = req.weightCheck.webpSize;
image.webpGain = req.weightCheck.bodySize - req.weightCheck.webpSize;
image.bestFormat = 'WebP';
image.maxGain = image.webpGain;
}
if (req.weightCheck.avifSize) {
image.avifSize = req.weightCheck.avifSize;
image.avifGain = req.weightCheck.bodySize - req.weightCheck.avifSize;
if (!req.weightCheck.webpSize || req.weightCheck.webpSize > req.weightCheck.avifSize) {
image.bestFormat = 'AVIF';
image.maxGain = image.avifGain;
}
}
results.totalGain += image.maxGain;
results.images.push(image);
}
});
return results;
}
function listImagesTooLarge(requests, device) {
var results = [];
@@ -889,9 +819,9 @@ var Redownload = function() {
debug('Downloading %s', entry.url);
// Always add compression and webp/avif headers before sending, in case the server listens to them
// Always add compression and webp headers before sending, in case the server listens to them
var reqHeaders = [];
reqHeaders['Accept'] = '*/*,image/webp,image/avif';
reqHeaders['Accept'] = '*/*,image/webp';
reqHeaders['Accept-Encoding'] = 'gzip, deflate, br';
reqHeaders['Connection'] = 'keep-alive';
reqHeaders['User-Agent'] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36';