Compare commits
6
Commits
images-beta-1
...
v3.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73c5b3383e | ||
|
|
5f09fb3bde | ||
|
|
0610fd6fbb | ||
|
|
22a8a206ab | ||
|
|
773249ca4f | ||
|
|
599d02ea0e |
+1
-3
@@ -33,11 +33,10 @@ var yellowLabTools = function(url, options) {
|
|||||||
|
|
||||||
// If a screenshot saveFunction was provided in the options
|
// If a screenshot saveFunction was provided in the options
|
||||||
if (options && typeof options.saveScreenshotFn === 'function') {
|
if (options && typeof options.saveScreenshotFn === 'function') {
|
||||||
const screenshotTmpPath = data.params.options.screenshot;
|
|
||||||
debug('Now optimizing screenshot...');
|
debug('Now optimizing screenshot...');
|
||||||
|
|
||||||
// TODO: temporarily set all screenshot sizes to 600px, until we find a solution
|
// TODO: temporarily set all screenshot sizes to 600px, until we find a solution
|
||||||
ScreenshotHandler.findAndOptimizeScreenshot(screenshotTmpPath, 600)
|
ScreenshotHandler.findAndOptimizeScreenshot(data.params.options.screenshot, 600)
|
||||||
|
|
||||||
.then(function(screenshotBuffer) {
|
.then(function(screenshotBuffer) {
|
||||||
debug('Screenshot optimized, now saving...');
|
debug('Screenshot optimized, now saving...');
|
||||||
@@ -51,7 +50,6 @@ var yellowLabTools = function(url, options) {
|
|||||||
|
|
||||||
// Remove uneeded temp screenshot path
|
// Remove uneeded temp screenshot path
|
||||||
delete data.params.options.screenshot;
|
delete data.params.options.screenshot;
|
||||||
return ScreenshotHandler.deleteTmpFile(screenshotTmpPath);
|
|
||||||
})
|
})
|
||||||
|
|
||||||
.catch(function(err) {
|
.catch(function(err) {
|
||||||
|
|||||||
@@ -729,7 +729,7 @@ var policies = {
|
|||||||
"totalWeight": {
|
"totalWeight": {
|
||||||
"tool": "redownload",
|
"tool": "redownload",
|
||||||
"label": "Total weight",
|
"label": "Total weight",
|
||||||
"message": "<p>The weight is of course very important if you want the page to load fast. Try to stay under 1.5MB.</p>",
|
"message": "<p>The weight is of course very important if you want the page to load fast. Try to stay under 1MB, which is already very long to download over a slow connection.</p>",
|
||||||
"isOkThreshold": 1572864,
|
"isOkThreshold": 1572864,
|
||||||
"isBadThreshold": 3145728,
|
"isBadThreshold": 3145728,
|
||||||
"isAbnormalThreshold": 5242880,
|
"isAbnormalThreshold": 5242880,
|
||||||
|
|||||||
@@ -1,19 +1,90 @@
|
|||||||
var debug = require('debug')('ylt:screenshotHandler');
|
var debug = require('debug')('ylt:screenshotHandlerAgent');
|
||||||
var sharp = require('sharp');
|
var Jimp = require('jimp');
|
||||||
var Q = require('q');
|
var Q = require('q');
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
|
|
||||||
// Disable sharp cache to reduce the "disk is full" error on Amazon Lambda
|
|
||||||
sharp.cache(false);
|
|
||||||
|
|
||||||
var screenshotHandler = function() {
|
var screenshotHandler = function() {
|
||||||
|
|
||||||
this.findAndOptimizeScreenshot = async function(tmpScreenshotPath, width) {
|
this.findAndOptimizeScreenshot = function(tmpScreenshotPath, width) {
|
||||||
return sharp(tmpScreenshotPath)
|
var that = this;
|
||||||
.resize({width: 600})
|
|
||||||
.jpeg({quality: 85})
|
debug('Starting screenshot transformation');
|
||||||
.toBuffer();
|
|
||||||
|
return this.openImage(tmpScreenshotPath)
|
||||||
|
|
||||||
|
.then(function(image) {
|
||||||
|
that.deleteTmpFile(tmpScreenshotPath);
|
||||||
|
return that.resizeImage(image, width);
|
||||||
|
})
|
||||||
|
|
||||||
|
.then(this.toBuffer);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
this.openImage = function(imagePath) {
|
||||||
|
var deferred = Q.defer();
|
||||||
|
|
||||||
|
Jimp.read(imagePath, function(err, image){
|
||||||
|
if (err) {
|
||||||
|
debug('Could not open imagePath %s', imagePath);
|
||||||
|
debug(err);
|
||||||
|
|
||||||
|
deferred.reject(err);
|
||||||
|
} else {
|
||||||
|
debug('Image correctly open');
|
||||||
|
deferred.resolve(image);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return deferred.promise;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
this.resizeImage = function(image, newWidth) {
|
||||||
|
var deferred = Q.defer();
|
||||||
|
|
||||||
|
var currentWidth = image.bitmap.width;
|
||||||
|
|
||||||
|
if (currentWidth > 0) {
|
||||||
|
var ratio = newWidth / currentWidth;
|
||||||
|
|
||||||
|
image.scale(ratio, function(err, image){
|
||||||
|
if (err) {
|
||||||
|
debug('Could not resize image');
|
||||||
|
debug(err);
|
||||||
|
|
||||||
|
deferred.reject(err);
|
||||||
|
} else {
|
||||||
|
debug('Image correctly resized');
|
||||||
|
deferred.resolve(image);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
deferred.reject('Could not resize an empty image');
|
||||||
|
}
|
||||||
|
|
||||||
|
return deferred.promise;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
this.toBuffer = function(image) {
|
||||||
|
var deferred = Q.defer();
|
||||||
|
|
||||||
|
image.quality(85).getBuffer(Jimp.MIME_JPEG, function(err, buffer){
|
||||||
|
if (err) {
|
||||||
|
debug('Could not save image to buffer');
|
||||||
|
debug(err);
|
||||||
|
|
||||||
|
deferred.reject(err);
|
||||||
|
} else {
|
||||||
|
debug('Image correctly transformed to buffer');
|
||||||
|
deferred.resolve(buffer);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return deferred.promise;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,20 @@
|
|||||||
var debug = require('debug')('ylt:contentTypeChecker');
|
var debug = require('debug')('ylt:contentTypeChecker');
|
||||||
var Q = require('q');
|
var Q = require('q');
|
||||||
var FileType = require('file-type');
|
var isJpg = require('is-jpg');
|
||||||
var isSvg = require('is-svg');
|
var isPng = require('is-png');
|
||||||
var isJson = require('is-json');
|
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() {
|
var ContentTypeChecker = function() {
|
||||||
|
|
||||||
async function checkContentType(entry) {
|
function checkContentType(entry) {
|
||||||
var deferred = Q.defer();
|
var deferred = Q.defer();
|
||||||
|
|
||||||
// Setting isSomething values:
|
// Setting isSomething values:
|
||||||
@@ -47,12 +55,12 @@ var ContentTypeChecker = function() {
|
|||||||
var foundType;
|
var foundType;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
foundType = await findContentType(entry.weightCheck.bodyBuffer);
|
foundType = findContentType(entry.weightCheck.bodyBuffer);
|
||||||
|
|
||||||
// If it's an image or a font, then rewrite.
|
// If it's an image or a font, then rewrite.
|
||||||
if (foundType !== null && (foundType.type === 'image' || foundType.type === 'webfont' || foundType.type === 'json')) {
|
if (foundType !== null && (foundType.type === 'image' || foundType.type === 'webfont' || foundType.type === 'json')) {
|
||||||
if (foundType.type !== entry.type) {
|
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);
|
rewriteContentType(entry, foundType);
|
||||||
}
|
}
|
||||||
@@ -68,21 +76,52 @@ var ContentTypeChecker = function() {
|
|||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function findContentType(bodyBuffer) {
|
function findContentType(bodyBuffer) {
|
||||||
var bodyStr = bodyBuffer.toString();
|
var bodyStr = bodyBuffer.toString();
|
||||||
|
|
||||||
|
if (isJpg(bodyBuffer)) {
|
||||||
|
return contentTypes.jpeg;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPng(bodyBuffer)) {
|
||||||
|
return contentTypes.png;
|
||||||
|
}
|
||||||
|
|
||||||
// https://github.com/sindresorhus/is-svg/issues/7
|
// https://github.com/sindresorhus/is-svg/issues/7
|
||||||
if (/<svg/.test(bodyStr) && isSvg(bodyStr)) {
|
if (/<svg/.test(bodyStr) && isSvg(bodyStr)) {
|
||||||
return contentTypes.svg;
|
return contentTypes.svg;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isJson(bodyStr)) {
|
if (isGif(bodyBuffer)) {
|
||||||
return contentTypes.json;
|
return contentTypes.gif;
|
||||||
}
|
}
|
||||||
|
|
||||||
const type = await FileType.fromBuffer(bodyBuffer);
|
if (isWebp(bodyBuffer)) {
|
||||||
if (type && type.ext && contentTypes[type.ext]) {
|
return contentTypes.webp;
|
||||||
return contentTypes[type.ext];
|
}
|
||||||
|
|
||||||
|
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;
|
return null;
|
||||||
@@ -107,7 +146,7 @@ var ContentTypeChecker = function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var contentTypes = {
|
var contentTypes = {
|
||||||
jpg: {
|
jpeg: {
|
||||||
type: 'image',
|
type: 'image',
|
||||||
mimes: ['image/jpeg'],
|
mimes: ['image/jpeg'],
|
||||||
updateFn: function(entry) {
|
updateFn: function(entry) {
|
||||||
@@ -148,14 +187,6 @@ var ContentTypeChecker = function() {
|
|||||||
entry.isImage = true;
|
entry.isImage = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
avif: {
|
|
||||||
type: 'image',
|
|
||||||
mimes: ['image/avif'],
|
|
||||||
updateFn: function(entry) {
|
|
||||||
entry.type = 'image';
|
|
||||||
entry.isImage = true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
woff: {
|
woff: {
|
||||||
type: 'webfont',
|
type: 'webfont',
|
||||||
mimes: ['application/x-font-woff', 'application/font-woff', 'font/woff'],
|
mimes: ['application/x-font-woff', 'application/font-woff', 'font/woff'],
|
||||||
|
|||||||
@@ -43,10 +43,6 @@ var ImageDimensions = function() {
|
|||||||
return entry.isImage && entry.contentType === 'image/png';
|
return entry.isImage && entry.contentType === 'image/png';
|
||||||
}
|
}
|
||||||
|
|
||||||
function isWebP(entry) {
|
|
||||||
return entry.isImage && entry.contentType === 'image/webp';
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getDimensions: getDimensions
|
getDimensions: getDimensions
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -819,9 +819,9 @@ var Redownload = function() {
|
|||||||
|
|
||||||
debug('Downloading %s', entry.url);
|
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 = [];
|
var reqHeaders = [];
|
||||||
reqHeaders['Accept'] = '*/*,image/webp,image/avif';
|
reqHeaders['Accept'] = '*/*,image/webp';
|
||||||
reqHeaders['Accept-Encoding'] = 'gzip, deflate, br';
|
reqHeaders['Accept-Encoding'] = 'gzip, deflate, br';
|
||||||
reqHeaders['Connection'] = 'keep-alive';
|
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';
|
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';
|
||||||
|
|||||||
+13
-4
@@ -16,7 +16,7 @@
|
|||||||
"yellowlabtools": "./bin/cli.js"
|
"yellowlabtools": "./bin/cli.js"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 16.0"
|
"node": ">= 18.0"
|
||||||
},
|
},
|
||||||
"main": "./lib/index.js",
|
"main": "./lib/index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -26,7 +26,6 @@
|
|||||||
"css-mq-parser": "0.0.3",
|
"css-mq-parser": "0.0.3",
|
||||||
"debug": "4.3.4",
|
"debug": "4.3.4",
|
||||||
"easyxml": "2.0.1",
|
"easyxml": "2.0.1",
|
||||||
"file-type": "16.5.3",
|
|
||||||
"fontkit": "2.0.2",
|
"fontkit": "2.0.2",
|
||||||
"html-minifier": "4.0.0",
|
"html-minifier": "4.0.0",
|
||||||
"image-size": "1.0.2",
|
"image-size": "1.0.2",
|
||||||
@@ -35,15 +34,25 @@
|
|||||||
"imagemin-jpegtran": "7.0.0",
|
"imagemin-jpegtran": "7.0.0",
|
||||||
"imagemin-optipng": "8.0.0",
|
"imagemin-optipng": "8.0.0",
|
||||||
"imagemin-svgo": "9.0.0",
|
"imagemin-svgo": "9.0.0",
|
||||||
|
"is-eot": "1.0.0",
|
||||||
|
"is-gif": "3.0.0",
|
||||||
|
"is-jpg": "2.0.0",
|
||||||
"is-json": "2.0.1",
|
"is-json": "2.0.1",
|
||||||
|
"is-otf": "0.1.2",
|
||||||
|
"is-png": "1.1.0",
|
||||||
"is-svg": "3.0.0",
|
"is-svg": "3.0.0",
|
||||||
|
"is-ttf": "0.2.2",
|
||||||
|
"is-webp": "1.0.1",
|
||||||
|
"is-woff": "1.0.3",
|
||||||
|
"is-woff2": "1.0.0",
|
||||||
|
"jimp": "0.22.8",
|
||||||
"md5": "2.3.0",
|
"md5": "2.3.0",
|
||||||
"meow": "5.0.0",
|
"meow": "5.0.0",
|
||||||
"parse-color": "1.0.0",
|
"parse-color": "1.0.0",
|
||||||
"phantomas": "gmetais/phantomas#gmetais-analyze-images",
|
"phantomas": "2.10.0",
|
||||||
"q": "1.5.1",
|
"q": "1.5.1",
|
||||||
"request": "2.88.2",
|
"request": "2.88.2",
|
||||||
"sharp": "0.32.3",
|
"sharp": "0.32.6",
|
||||||
"ttf2woff2": "5.0.0",
|
"ttf2woff2": "5.0.0",
|
||||||
"uglify-js": "3.17.4",
|
"uglify-js": "3.17.4",
|
||||||
"woff-tools": "0.1.0"
|
"woff-tools": "0.1.0"
|
||||||
|
|||||||
@@ -10,11 +10,11 @@ describe('contentTypeChecker', function() {
|
|||||||
var svgImageContent = fs.readFileSync(path.resolve(__dirname, '../www/svg-image.svg'));
|
var svgImageContent = fs.readFileSync(path.resolve(__dirname, '../www/svg-image.svg'));
|
||||||
var cssFileContent = fs.readFileSync(path.resolve(__dirname, '../www/unminified-stylesheet.css'));
|
var cssFileContent = fs.readFileSync(path.resolve(__dirname, '../www/unminified-stylesheet.css'));
|
||||||
|
|
||||||
it('detect the right content type', async function() {
|
it('detect the right content type', function() {
|
||||||
(await contentTypeChecker.findContentType(jpgImageContent)).mimes.should.deep.equal(['image/jpeg']);
|
contentTypeChecker.findContentType(jpgImageContent).mimes.should.deep.equal(['image/jpeg']);
|
||||||
(await contentTypeChecker.findContentType(pngImageContent)).mimes.should.deep.equal(['image/png']);
|
contentTypeChecker.findContentType(pngImageContent).mimes.should.deep.equal(['image/png']);
|
||||||
(await contentTypeChecker.findContentType(svgImageContent)).mimes.should.deep.equal(['image/svg+xml']);
|
contentTypeChecker.findContentType(svgImageContent).mimes.should.deep.equal(['image/svg+xml']);
|
||||||
should.equal(await contentTypeChecker.findContentType(cssFileContent), null);
|
should.equal(contentTypeChecker.findContentType(cssFileContent), null);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -160,9 +160,18 @@ describe('redownload', function() {
|
|||||||
redownload.redownloadEntry(entry)
|
redownload.redownloadEntry(entry)
|
||||||
|
|
||||||
.then(function(newEntry) {
|
.then(function(newEntry) {
|
||||||
|
|
||||||
newEntry.weightCheck.bodySize.should.equal(4193);
|
newEntry.weightCheck.bodySize.should.equal(4193);
|
||||||
newEntry.weightCheck.bodyBuffer.should.deep.equal(fileContent);
|
newEntry.weightCheck.bodyBuffer.should.deep.equal(fileContent);
|
||||||
done();
|
|
||||||
|
// Opening the image in jimp to check if the format is good
|
||||||
|
var Jimp = require('jimp');
|
||||||
|
Jimp.read(newEntry.weightCheck.bodyBuffer, function(err, image) {
|
||||||
|
image.bitmap.width.should.equal(620);
|
||||||
|
image.bitmap.height.should.equal(104);
|
||||||
|
done(err);
|
||||||
|
});
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
.fail(function(err) {
|
.fail(function(err) {
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 36 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 82 KiB |
Reference in New Issue
Block a user