Compare commits

...
9 Commits
Author SHA1 Message Date
Gaël MétaisandGitHub 8f5f9d6238 1.13.4 (#305)
* Update GitHub repository URL
* Update jQuery version
2020-02-04 12:40:57 -03:00
PurpleBabarandGaël Métais 52ac5e5424 Dockerize app (#303)
Add a Dockerfile to the project
2020-01-17 11:10:53 -03:00
Gaël Métais c59e9fc3f5 Merge branch 'develop' 2019-01-13 17:39:27 +05:30
Gaël Métais febe9871b9 Fix missing text 2019-01-13 17:39:11 +05:30
Gaël Métais 4d5d881af6 Merge branch 'develop' 2019-01-13 17:21:48 +05:30
Gaël Métais 2735deff73 v1.13.3 2019-01-13 17:21:29 +05:30
Gaël Métais ee93add6fc Remove failing 4.7 tests on Travis 2019-01-13 17:19:54 +05:30
Gaël MétaisandGitHub 8ffab2ae70 New rule: oversized images (#276)
New rule: oversized images
2019-01-13 17:18:03 +05:30
Gaël Métais fa216dddd5 Fix typo 2019-01-07 12:59:57 +05:30
19 changed files with 8274 additions and 20 deletions
+1
View File
@@ -0,0 +1 @@
node_modules/
+1 -1
View File
@@ -4,5 +4,5 @@ node_modules
results/*
coverage
front/build
package-lock.json
har.json
-1
View File
@@ -3,7 +3,6 @@ sudo: false
node_js:
- "8.9"
- "6.2"
- "4.7"
env:
- CXX=g++-4.8
addons:
+7
View File
@@ -0,0 +1,7 @@
FROM node:10
WORKDIR /app
ENV VERSION=master
EXPOSE 8383
RUN git clone --branch ${VERSION} https://github.com/LumberjackOtters/YellowLabTools ylt && cd ylt && yarn install && yarn build
ENV NODE_ENV=production
CMD ["node", "/app/ylt/bin/server.js"]
+4 -4
View File
@@ -19,7 +19,7 @@ Analyzes a webpage and detects **performance** or **front-end code quality** iss
</tr>
<tr>
<td width="70%">
The <b>CLI</b> (Command Line Interface) - <a href="https://github.com/gmetais/YellowLabTools/wiki/Command-Line-Interface" target="_blank">Doc here</a>
The <b>CLI</b> (Command Line Interface) - <a href="https://github.com/YellowLabTools/YellowLabTools/wiki/Command-Line-Interface" target="_blank">Doc here</a>
</td>
<td width="30%">
<img src="./doc/img/YLT-cli-animated.gif"></img>
@@ -36,7 +36,7 @@ Analyzes a webpage and detects **performance** or **front-end code quality** iss
</tr>
<tr>
<td width="70%">
The <b>NodeJS</b> package - <a href="https://github.com/gmetais/YellowLabTools/wiki/NodeJS-module" target="_blank">Doc here</a>
The <b>NodeJS</b> package - <a href="https://github.com/YellowLabTools/YellowLabTools/wiki/NodeJS-module" target="_blank">Doc here</a>
</td>
<td width="30%">
<img src="./doc/img/npm-logo.png"></img>
@@ -44,7 +44,7 @@ Analyzes a webpage and detects **performance** or **front-end code quality** iss
</tr>
<tr>
<td width="70%">
The <b>public API</b> - <a href="https://github.com/gmetais/YellowLabTools/wiki/Public-API" target="_blank">Doc here</a>
The <b>public API</b> - <a href="https://github.com/YellowLabTools/YellowLabTools/wiki/Public-API" target="_blank">Doc here</a>
<br>
Allows you to launch runs from any language
</td>
@@ -72,7 +72,7 @@ You can use [ngrok](https://ngrok.com/), a tool that creates a secure tunnel bet
## Install your own private instance
If your project is not accessible from outside, or if you want to fork and improve the tool, you can build your own instance. The documentation is [here](https://github.com/gmetais/YellowLabTools/wiki/Install-your-private-server).
If your project is not accessible from outside, or if you want to fork and improve the tool, you can build your own instance. The documentation is [here](https://github.com/YellowLabTools/YellowLabTools/wiki/Install-your-private-server).
## Author
+2 -2
View File
@@ -261,8 +261,8 @@
}
.smallPreview {
display: block;
max-height: 4em;
max-width: 8em;
max-height: 6em;
max-width: 16em;
border: 1px solid #000;
margin: 1em auto 0.2em;
}
+1 -1
View File
@@ -42,7 +42,7 @@ yltApp.run(['$rootScope', '$location', function($rootScope, $location) {
// GitHub star button (asynchronously loaded iframe)
window.addEventListener('load', function() {
window.document.getElementById('ghbtn').src = 'https://ghbtns.com/github-btn.html?user=gmetais&repo=YellowLabTools&type=star&count=true&size=large';
window.document.getElementById('ghbtn').src = 'https://ghbtns.com/github-btn.html?user=YellowLabTools&repo=YellowLabTools&type=star&count=true&size=large';
});
}]);
+2 -2
View File
@@ -290,8 +290,8 @@
.smallPreview {
display: block;
max-height: 4em;
max-width: 8em;
max-height: 6em;
max-width: 16em;
border: 1px solid #000;
margin: 1em auto 0.2em;
}
+1 -1
View File
@@ -3,7 +3,7 @@
<p>This is done by loading the webpage via PhantomJS and collecting various metrics and statistics with the help of <a href="https://github.com/macbre/phantomas" target="_blank">Phantomas</a>. These metrics are categorized and transformed into scores. It also gives in-depth details so that developers can correct the detected issues.</p>
<p>By the way, <b>it's free</b> because we are geeks, not businessmen. All we want is <a href="https://github.com/gmetais/YellowLabTools" target="_blank" class="star">a <span>&#9733;</span> on GitHub</a>. It will boost our motivation to add more awesome features!!!</p>
<p>By the way, <b>it's free</b> because we are geeks, not businessmen. All we want is <a href="https://github.com/YellowLabTools/YellowLabTools" target="_blank" class="star">a <span>&#9733;</span> on GitHub</a>. It will boost our motivation to add more awesome features!!!</p>
<%if (sponsoring.about) { %>
<div class="sponsor"><%- sponsoring.about %></div>
+1 -1
View File
@@ -5,7 +5,7 @@
<div class="status">Test failed</div>
<p class="statusSubMessage">{{status.error}}</p>
<a class="linkButton" href="https://github.com/gmetais/YellowLabTools/issues" target="_blank">Report the issue on GitHub</a>
<a class="linkButton" href="https://github.com/YellowLabTools/YellowLabTools/issues" target="_blank">Report the issue on GitHub</a>
<a class="linkButton" href="<%= baseUrl %>">New test</a>
</div>
<div ng-if="status.statusCode == 'awaiting'">
+6
View File
@@ -137,6 +137,12 @@
<url-link url="offender" max-length="100"></url-link>
</div>
<div ng-if="policyName === 'imagesTooLarge'">
<img ng-src="{{offender.url | https}}" class="smallPreview checker"></img>
<div>{{offender.width}}x{{offender.height}}</div>
<url-link url="offender.url" max-length="100"></url-link>
</div>
<div ng-if="policyName === 'notFound' || policyName === 'emptyRequests' || policyName === 'closedConnections' || policyName === 'multipleRequests' || policyName === 'cachingDisabled' || policyName === 'cachingNotSpecified'">
<url-link url="offender" max-length="100"></url-link>
</div>
+13 -2
View File
@@ -276,7 +276,7 @@ var policies = {
},
"jQueryVersion": {
"label": "jQuery version",
"message": "<p>The current latest version of jQuery is 3.3</p><p>Each new version of jQuery optimizes performances. Do not keep an old version of jQuery. Updating can sometimes break a few things, but it is generally quite easy to fix them up. So don't hesitate.</p>",
"message": "<p>The current latest version of jQuery is 3.4</p><p>Each new version of jQuery optimizes performances. Do not keep an old version of jQuery. Updating can sometimes break a few things, but it is generally quite easy to fix them up. So don't hesitate.</p>",
"hasOffenders": false,
"scoreFn": function(data) {
var differentVersions = data.toolsResults.phantomas.metrics.jQueryVersionsLoaded;
@@ -291,6 +291,8 @@ var policies = {
if (value.indexOf('3.3.') === 0 ||
value.indexOf('3.4.') === 0 ||
value.indexOf('3.5.') === 0 ||
value.indexOf('3.6.') === 0 ||
value.indexOf('3.7.') === 0 ||
value.indexOf('4.0.') === 0) {
score = 100;
} else if (value.indexOf('3.2.') === 0) {
@@ -914,6 +916,15 @@ var policies = {
"hasOffenders": true,
"unit": 'bytes'
},
"imagesTooLarge": {
"tool": "redownload",
"label": "Oversized images",
"message": "<p>This is the number of images with a width >800px on mobile or >1500px on desktop. Try reducing their size.</p><p>Please ignore if the file is used as a sprite.</p><p>Please note that Yellow Lab Tools' engine (PhantomJS) is not compatible with image srcset (unless you use a polyfill). This can lead to incorrect detection.</p>",
"isOkThreshold": 0,
"isBadThreshold": 5,
"isAbnormalThreshold": 10,
"hasOffenders": true
},
"gzipCompression": {
"tool": "redownload",
"label": "Gzip compression",
@@ -927,7 +938,7 @@ var policies = {
"fileMinification": {
"tool": "redownload",
"label": "File minification",
"message": "<p>This is the weight that could be saved if all text resources were correctly minified.</p><p>The tools in use here are <b>UglifyJS</b>, <b>clean-css</b> and <b>HTMLMinifier</b>. These tools are so good that some of your minified files can be marked as unminified. Change your tool it this happens :)</p><p>The gains of minification are generally small, but the impact can be high when these text files are loaded on the critical path.</p>",
"message": "<p>This is the weight that could be saved if all text resources were correctly minified.</p><p>The tools in use here are <b>UglifyJS</b>, <b>clean-css</b> and <b>HTMLMinifier</b>. These tools are so good that some of your minified files can be marked as unminified. Change your tool if this happens :)</p><p>The gains of minification are generally small, but the impact can be high when these text files are loaded on the critical path.</p>",
"isOkThreshold": 5125,
"isBadThreshold": 61440,
"isAbnormalThreshold": 122880,
+1
View File
@@ -5,6 +5,7 @@
"policies": {
"totalWeight": 5,
"imageOptimization": 2,
"imagesTooLarge": 2,
"gzipCompression": 2,
"fileMinification": 1
}
+51
View File
@@ -0,0 +1,51 @@
var debug = require('debug')('ylt:imageDimensions');
var Q = require('q');
var sizeOf = require('image-size');
var ImageDimensions = function() {
function getDimensions(entry) {
var deferred = Q.defer();
if (!entry.weightCheck || !entry.weightCheck.bodyBuffer) {
// No valid file available
deferred.resolve(entry);
return deferred.promise;
}
var fileSize = entry.weightCheck.uncompressedSize;
if (isJPEG(entry) || isPNG(entry)) {
try {
var dimensions = sizeOf(entry.weightCheck.bodyBuffer);
debug('Image dimensions of %s: %sx%s', entry.url, dimensions.width, dimensions.height);
entry.imageDimensions = {
width: dimensions.width,
height: dimensions.height
};
} catch(err) {
debug('Error while checking image dimensions:');
debug(err);
}
}
deferred.resolve(entry);
return deferred.promise;
}
function isJPEG(entry) {
return entry.isImage && entry.contentType === 'image/jpeg';
}
function isPNG(entry) {
return entry.isImage && entry.contentType === 'image/png';
}
return {
getDimensions: getDimensions
};
};
module.exports = new ImageDimensions();
+30 -2
View File
@@ -19,6 +19,7 @@ var fileMinifier = require('./fileMinifier');
var gzipCompressor = require('./gzipCompressor');
var contentTypeChecker = require('./contentTypeChecker');
var fontAnalyzer = require('./fontAnalyzer');
var imageDimensions = require('./imageDimensions');
var Redownload = function() {
@@ -69,6 +70,8 @@ var Redownload = function() {
.then(imageOptimizer.optimizeImage)
.then(imageDimensions.getDimensions)
.then(fileMinifier.minifyFile)
.then(gzipCompressor.compressFile)
@@ -142,9 +145,14 @@ var Redownload = function() {
// Image compression
offenders.imageOptimization = listImageNotOptimized(results);
offenders.imageOptimization = listImagesNotOptimized(results);
metrics.imageOptimization = offenders.imageOptimization.totalGain;
// Image width
var isMobile = data.params.options.device === 'phone';
offenders.imagesTooLarge = listImagesTooLarge(results, isMobile);
metrics.imagesTooLarge = offenders.imagesTooLarge.length;
// File minification
offenders.fileMinification = listFilesNotMinified(results);
metrics.fileMinification = offenders.fileMinification.totalGain;
@@ -273,7 +281,7 @@ var Redownload = function() {
}
function listImageNotOptimized(requests) {
function listImagesNotOptimized(requests) {
var results = {
totalGain: 0,
images: []
@@ -305,6 +313,26 @@ var Redownload = function() {
return results;
}
function listImagesTooLarge(requests, isMobile) {
var results = [];
requests.forEach(function(req) {
if (req.weightCheck.bodySize > 0 &&
req.imageDimensions &&
((isMobile && req.imageDimensions.width > 800) || req.imageDimensions.width > 1500)) {
results.push({
url: req.url,
weight: req.weightCheck.bodySize,
width: req.imageDimensions.width,
height: req.imageDimensions.height
});
}
});
return results;
}
function listFilesNotMinified(requests) {
var results = {
+5 -3
View File
@@ -1,6 +1,6 @@
{
"name": "yellowlabtools",
"version": "1.13.2",
"version": "1.13.4",
"description": "Online tool to audit a webpage for performance and front-end quality issues",
"license": "GPL-2.0",
"author": {
@@ -10,7 +10,7 @@
},
"repository": {
"type": "git",
"url": "git://github.com/gmetais/YellowLabTools.git"
"url": "git://github.com/YellowLabTools/YellowLabTools.git"
},
"bin": {
"yellowlabtools": "./bin/cli.js"
@@ -40,6 +40,7 @@
"ejs": "2.5.7",
"express": "4.16.2",
"fontkit": "1.7.7",
"image-size": "0.7.1",
"imagemin": "5.3.1",
"imagemin-jpegoptim": "5.2.0",
"imagemin-jpegtran": "5.0.2",
@@ -96,7 +97,8 @@
"sinon-chai": "~2.14.0"
},
"scripts": {
"test": "grunt test"
"test": "grunt test",
"build": "grunt build"
},
"keywords": [
"performance",
+90
View File
@@ -0,0 +1,90 @@
var should = require('chai').should();
var imageDimensions = require('../../lib/tools/redownload/imageDimensions');
var fs = require('fs');
var path = require('path');
describe('imageDimensions', function() {
it('should detect png image dimensions', function(done) {
var fileContent = fs.readFileSync(path.resolve(__dirname, '../www/png-image.png'));
var entry = {
method: 'GET',
url: 'http://localhost:8388/an-image.png',
requestHeaders: {
'User-Agent': 'something',
Referer: 'http://www.google.fr/',
Accept: '*/*',
'Accept-Encoding': 'gzip, deflate'
},
status: 200,
isImage: true,
type: 'image',
contentType: 'image/png',
contentLength: 999,
weightCheck: {
bodyBuffer: fileContent,
totalWeight: 999,
headersSize: 200,
bodySize: 999,
isCompressed: false,
uncompressedSize: 999
}
};
imageDimensions.getDimensions(entry)
.then(function(newEntry) {
newEntry.should.have.a.property('imageDimensions');
newEntry.imageDimensions.should.have.a.property('width').that.equals(664);
newEntry.imageDimensions.should.have.a.property('height').that.equals(314);
done();
})
.fail(function(err) {
done(err);
});
});
it('should detect a jpg image dimensions', function(done) {
var fileContent = fs.readFileSync(path.resolve(__dirname, '../www/jpeg-image.jpg'));
var entry = {
method: 'GET',
url: 'http://localhost:8388/an-image.jpg',
requestHeaders: {
'User-Agent': 'something',
Referer: 'http://www.google.fr/',
Accept: '*/*',
'Accept-Encoding': 'gzip, deflate'
},
status: 200,
isImage: true,
type: 'image',
contentType: 'image/jpeg',
contentLength: 999,
weightCheck: {
bodyBuffer: fileContent,
totalWeight: 999,
headersSize: 200,
bodySize: 999,
isCompressed: false,
uncompressedSize: 999
}
};
imageDimensions.getDimensions(entry)
.then(function(newEntry) {
newEntry.should.have.a.property('imageDimensions');
newEntry.imageDimensions.should.have.a.property('width').that.equals(285);
newEntry.imageDimensions.should.have.a.property('height').that.equals(427);
done();
})
.fail(function(err) {
done(err);
});
});
});
+8
View File
@@ -109,6 +109,11 @@ describe('redownload', function() {
];
var data = {
params: {
options: {
device: 'phone'
}
},
toolsResults: {
phantomas: {
metrics: {
@@ -140,6 +145,9 @@ describe('redownload', function() {
data.toolsResults.redownload.offenders.imageOptimization.totalGain.should.be.above(0);
data.toolsResults.redownload.offenders.imageOptimization.images.length.should.equal(2);
data.toolsResults.redownload.offenders.should.have.a.property('imagesTooLarge');
data.toolsResults.redownload.offenders.imagesTooLarge.length.should.equal(0);
data.toolsResults.redownload.offenders.should.have.a.property('gzipCompression');
data.toolsResults.redownload.offenders.gzipCompression.totalGain.should.be.above(0);
data.toolsResults.redownload.offenders.gzipCompression.files.length.should.equal(5);
+8050
View File
File diff suppressed because it is too large Load Diff