Merge pull request #32 from gmetais/phantomas1.8

Update phantomas to v1.8
This commit is contained in:
Gaël Métais
2014-12-29 12:53:26 +01:00
17 changed files with 118 additions and 206 deletions
+3 -3
View File
@@ -1,8 +1,8 @@
{
"name": "yellowlabtools",
"dependencies": {
"angular": "~1.3.5",
"angular-route": "~1.3.6",
"angular-resource": "~1.3.6"
"angular": "~1.3.8",
"angular-route": "~1.3.8",
"angular-resource": "~1.3.7"
}
}
+32
View File
@@ -58,6 +58,14 @@ var policies = {
"isAbnormalThreshold": 500,
"takeOffendersFrom": "DOMqueriesDuplicated"
},
"DOMqueriesWithoutResults": {
"tool": "phantomas",
"label": "DOM queries without result",
"message": "<p>Number of queries that return no result.</p><p>It suggests the query is not used on the page, probably because it is some dead code.</p><p>Or maybe the code is trying to find an HTML block that is not always here. Look at the JS Timeline to see if the scripts correctly figures out the HTML block is not here and immediatly stops interacting further with the DOM.</p>",
"isOkThreshold": 0,
"isBadThreshold": 100,
"isAbnormalThreshold": 200
},
"eventsBound": {
"tool": "phantomas",
"label": "Events bound",
@@ -359,6 +367,30 @@ var policies = {
"isBadThreshold": 20,
"isAbnormalThreshold": 40
},
"smallJsFiles": {
"tool": "phantomas",
"label": "Small JS files",
"message": "<p>Number of JS assets smaller than 2 KB that could probably be inlined or merged.</p>",
"isOkThreshold": 2,
"isBadThreshold": 10,
"isAbnormalThreshold": 16
},
"smallCssFiles": {
"tool": "phantomas",
"label": "Small CSS files",
"message": "<p>Number of CSS assets smaller than 2 KB that could probably be inlined or merged.</p>",
"isOkThreshold": 0,
"isBadThreshold": 8,
"isAbnormalThreshold": 12
},
"smallImages": {
"tool": "phantomas",
"label": "Small images",
"message": "<p>Images smaller than 2 KB that could be base64 encoded or merged into a sprite.</p>",
"isOkThreshold": 2,
"isBadThreshold": 17,
"isAbnormalThreshold": 30
},
"notFound": {
"tool": "phantomas",
"label": "404 not found",
+10
View File
@@ -14,6 +14,7 @@
"policies": {
"DOMinserts": 2,
"DOMqueries": 1,
"DOMqueriesWithoutResults": 2,
"DOMqueriesAvoidable": 2,
"eventsBound": 1
}
@@ -79,6 +80,14 @@
"otherCount": 0
}
},
"smallRequests": {
"label": "Small requests",
"policies": {
"smallJsFiles": 1,
"smallCssFiles": 1,
"smallImages": 1
}
},
"network": {
"label": "Network",
"policies": {
@@ -100,6 +109,7 @@
"cssComplexity": 1,
"badCSS": 1,
"requests": 3,
"smallRequests": 1,
"network": 2
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
var Q = require('q');
var debug = require('debug')('ylt:runner');
var phantomasWrapper = require('./tools/phantomasWrapper');
var phantomasWrapper = require('./tools/phantomas/phantomasWrapper');
var jsExecutionTransformer = require('./tools/jsExecutionTransformer');
var rulesChecker = require('./rulesChecker');
var scoreCalculator = require('./scoreCalculator');
@@ -7,7 +7,7 @@
*/
/* global document: true, window: true */
exports.version = '0.1';
exports.version = '0.2';
exports.module = function(phantomas) {
'use strict';
@@ -62,7 +62,7 @@ exports.module = function(phantomas) {
// After
if (enabled && callbackAfter) {
callbackAfter.call(this, result);
callbackAfter.call(this, result, arguments);
}
}
@@ -3,12 +3,13 @@
*/
/* global Element: true, Document: true, Node: true, window: true */
exports.version = '0.9.a';
exports.version = '0.10.a';
exports.module = function(phantomas) {
'use strict';
phantomas.setMetric('DOMqueries'); // @desc number of all DOM queries @offenders
phantomas.setMetric('DOMqueriesWithoutResults'); // @desc number of DOM queries that retutned nothing @offenders
phantomas.setMetric('DOMqueriesById'); // @desc number of document.getElementById calls
phantomas.setMetric('DOMqueriesByClassName'); // @desc number of document.getElementsByClassName calls
phantomas.setMetric('DOMqueriesByTagName'); // @desc number of document.getElementsByTagName calls
@@ -21,14 +22,13 @@ exports.module = function(phantomas) {
phantomas.once('init', function() {
phantomas.evaluate(function() {
(function(phantomas) {
function querySpy(type, query, fnName, context) {
phantomas.emit('domQuery', type, query, fnName, context); // @desc DOM query has been made
function querySpy(type, query, fnName, context, hasNoResults) {
phantomas.emit('domQuery', type, query, fnName, context, hasNoResults); // @desc DOM query has been made
}
phantomas.spy(Document.prototype, 'getElementById', function(id) {
phantomas.incrMetric('DOMqueriesById');
phantomas.addOffender('DOMqueriesById', '#%s (in %s)', id, '#document');
querySpy('id', '#' + id, 'getElementById', '#document');
phantomas.enterContext({
type: 'getElementById',
@@ -41,9 +41,13 @@ exports.module = function(phantomas) {
backtrace: phantomas.getBacktrace()
});
}, function(result) {
}, function(result, args) {
var id = args[0];
querySpy('id', '#' + id, 'getElementById', '#document', (result === null));
var moreData = {
resultsNumber : result ? 1 : 0
resultsNumber : (result === null) ? 0 : 1
};
phantomas.leaveContext(moreData);
});
@@ -56,7 +60,6 @@ exports.module = function(phantomas) {
phantomas.incrMetric('DOMqueriesByClassName');
phantomas.addOffender('DOMqueriesByClassName', '.%s (in %s)', className, context);
querySpy('class', '.' + className, 'getElementsByClassName', context);
phantomas.enterContext({
type: 'getElementsByClassName',
@@ -70,7 +73,14 @@ exports.module = function(phantomas) {
});
}
function selectorClassNameAfter(result) {
function selectorClassNameAfter(result, args) {
/*jshint validthis: true */
var className = args[0];
var context = phantomas.getDOMPath(this);
querySpy('class', '.' + className, 'getElementsByClassName', context, (result.length === 0));
var moreData = {
resultsNumber : (result && result.length > 0) ? result.length : 0
};
@@ -88,7 +98,6 @@ exports.module = function(phantomas) {
phantomas.incrMetric('DOMqueriesByTagName');
phantomas.addOffender('DOMqueriesByTagName', '%s (in %s)', tagName, context);
querySpy('tag name', tagName.toLowerCase(), 'getElementsByTagName', context);
phantomas.enterContext({
type: 'getElementsByTagName',
@@ -102,7 +111,14 @@ exports.module = function(phantomas) {
});
}
function selectorTagNameSpyAfter(result) {
function selectorTagNameSpyAfter(result, args) {
/*jshint validthis: true */
var tagName = args[0];
var context = phantomas.getDOMPath(this);
querySpy('tag name', tagName.toLowerCase(), 'getElementsByTagName', context, (result.length === 0));
var moreData = {
resultsNumber : (result && result.length > 0) ? result.length : 0
};
@@ -112,11 +128,11 @@ exports.module = function(phantomas) {
phantomas.spy(Document.prototype, 'getElementsByTagName', selectorTagNameSpyBefore, selectorTagNameSpyAfter);
phantomas.spy(Element.prototype, 'getElementsByTagName', selectorTagNameSpyBefore, selectorTagNameSpyAfter);
// selector queries
function selectorQuerySpy(selector, context) {
phantomas.incrMetric('DOMqueriesByQuerySelectorAll');
phantomas.addOffender('DOMqueriesByQuerySelectorAll', '%s (in %s)', selector, context);
querySpy('selector', selector, 'querySelectorAll', context);
}
function selectorQuerySpyBefore(selector) {
@@ -137,7 +153,14 @@ exports.module = function(phantomas) {
});
}
function selectorQuerySpyAfter(result) {
function selectorQuerySpyAfter(result, args) {
/*jshint validthis: true */
var selector = args[0];
var context = phantomas.getDOMPath(this);
querySpy('selector', selector, 'querySelectorAll', context, (!result || result.length === 0));
var moreData = {
resultsNumber : result ? 1 : 0
};
@@ -162,7 +185,14 @@ exports.module = function(phantomas) {
});
}
function selectorAllQuerySpryAfter(result) {
function selectorAllQuerySpryAfter(result, args) {
/*jshint validthis: true */
var selector = args[0];
var context = phantomas.getDOMPath(this);
querySpy('selector', selector, 'querySelectorAll', context, (!result || result.length === 0));
var moreData = {
resultsNumber : (result && result.length > 0) ? result.length : 0
};
@@ -244,9 +274,22 @@ exports.module = function(phantomas) {
});
});
// report DOM queries that return no results (issue #420)
phantomas.on('domQuery', function(type, query, fnName, context, hasNoResults) {
// ignore DOM queries within DOM fragments (used internally by jQuery)
if (context.indexOf('body') !== 0 && context.indexOf('#document') !== 0) {
return;
}
if (hasNoResults === true) {
phantomas.incrMetric('DOMqueriesWithoutResults');
phantomas.addOffender('DOMqueriesWithoutResults', '%s (in %s) using %s', query, context, fnName);
}
});
// count DOM queries by either ID, tag name, class name and selector query
// @see https://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-document-doctype
var Collection = require('../../../node_modules/phantomas/lib/collection'),
var Collection = require('../../../../../../node_modules/phantomas/lib/collection'),
DOMqueries = new Collection();
phantomas.on('domQuery', function(type, query, fnName, context) {
@@ -298,7 +298,7 @@ exports.module = function(phantomas) {
// count Sizzle calls to detect duplicated queries
var Collection = require('../../../node_modules/phantomas/lib/collection'),
var Collection = require('../../../../../../node_modules/phantomas/lib/collection'),
sizzleCalls = new Collection(),
jQueryLoading = new Collection();
@@ -31,7 +31,6 @@ var PhantomasWrapper = function() {
'analyze-css': true,
'skip-modules': [
'blockDomains', // not needed
'domComplexity', // overriden
'domMutations', // not compatible with webkit
'domQueries', // overriden
'eventListeners', // overridden
@@ -43,8 +42,8 @@ var PhantomasWrapper = function() {
'windowPerformance' // overriden
].join(','),
'include-dirs': [
'phantomas_custom/core',
'phantomas_custom/modules'
'lib/tools/phantomas/custom_modules/core',
'lib/tools/phantomas/custom_modules/modules'
].join(',')
};
+9 -9
View File
@@ -12,12 +12,12 @@
"dependencies": {
"async": "~0.9.0",
"body-parser": "~1.10.0",
"compression": "~1.2.1",
"debug": "^2.1.0",
"express": "~4.10.4",
"phantomas": "1.7.0",
"rimraf": "^2.2.8",
"q": "^1.1.2"
"compression": "~1.2.2",
"debug": "~2.1.0",
"express": "~4.10.6",
"phantomas": "1.8.0",
"rimraf": "~2.2.8",
"q": "~1.1.2"
},
"devDependencies": {
"chai": "^1.10.0",
@@ -31,9 +31,9 @@
"grunt-fontsmith": "^0.9.1",
"grunt-mocha-test": "^0.12.4",
"matchdep": "^0.3.0",
"mocha": "^2.0.1",
"phantomjs": "^1.9.12",
"request": "^2.49.0",
"mocha": "^2.1.0",
"phantomjs": "^1.9.13",
"request": "^2.51.0",
"sinon": "^1.12.1",
"sinon-chai": "^2.6.0"
},
@@ -1,135 +0,0 @@
/**
* Analyzes DOM complexity
*/
/* global document: true, Node: true, window: true */
exports.version = '1.0.a';
exports.module = function(phantomas) {
'use strict';
// total length of HTML comments (including <!-- --> brackets)
phantomas.setMetric('commentsSize'); // @desc the size of HTML comments on the page @offenders
// total length of text nodes with whitespaces only (i.e. pretty formatting of HTML)
phantomas.setMetric('whiteSpacesSize'); // @desc the size of text nodes with whitespaces only
// count all tags
phantomas.setMetric('DOMelementsCount'); // @desc total number of HTML element nodes
phantomas.setMetric('DOMelementMaxDepth'); // @desc maximum level on nesting of HTML element node
// nodes with inlines CSS (style attribute)
phantomas.setMetric('nodesWithInlineCSS'); // @desc number of nodes with inline CSS styling (with style attribute) @offenders
// images
phantomas.setMetric('imagesScaledDown'); // @desc number of <img> nodes that have images scaled down in HTML @offenders
phantomas.setMetric('imagesWithoutDimensions'); // @desc number of <img> nodes without both width and height attribute @offenders
// duplicated ID (issue #392)
phantomas.setMetric('DOMidDuplicated'); // @desc number of duplicated IDs found in DOM
var Collection = require('../../../node_modules/phantomas/lib/collection'),
DOMids = new Collection();
phantomas.on('domId', function(id) {
DOMids.push(id);
});
// HTML size
phantomas.on('report', function() {
phantomas.setMetricEvaluate('bodyHTMLSize', function() { // @desc the size of body tag content (document.body.innerHTML.length)
return document.body && document.body.innerHTML.length || 0;
});
phantomas.evaluate(function() {
(function(phantomas) {
var runner = new phantomas.nodeRunner(),
whitespacesRegExp = /^\s+$/,
DOMelementMaxDepth = 0,
DOMelementMaxDepthElts = [],
size = 0;
runner.walk(document.body, function(node, depth) {
switch (node.nodeType) {
case Node.COMMENT_NODE:
size = node.textContent.length + 7; // '<!--' + '-->'.length
phantomas.incrMetric('commentsSize', size);
// log HTML comments bigger than 64 characters
if (size > 64) {
phantomas.addOffender('commentsSize', phantomas.getDOMPath(node) + ' (' + size + ' characters)');
}
break;
case Node.ELEMENT_NODE:
phantomas.incrMetric('DOMelementsCount');
if (depth > DOMelementMaxDepth) {
DOMelementMaxDepth = depth;
DOMelementMaxDepthElts = [phantomas.getDOMPath(node)];
} else if (depth === DOMelementMaxDepth) {
DOMelementMaxDepthElts.push(phantomas.getDOMPath(node));
}
// report duplicated ID (issue #392)
if (node.id) {
phantomas.emit('domId', node.id);
}
// ignore inline <script> tags
if (node.nodeName === 'SCRIPT') {
return false;
}
// images
if (node.nodeName === 'IMG') {
if (!node.hasAttribute('width') || !node.hasAttribute('height')) {
phantomas.incrMetric('imagesWithoutDimensions');
phantomas.addOffender('imagesWithoutDimensions', '%s <%s>', phantomas.getDOMPath(node), node.src);
}
if (node.naturalHeight && node.naturalWidth && node.height && node.width) {
if (node.naturalHeight > node.height || node.naturalWidth > node.width) {
phantomas.incrMetric('imagesScaledDown');
phantomas.addOffender('imagesScaledDown', '%s (%dx%d -> %dx%d)', node.src, node.naturalWidth, node.naturalHeight, node.width, node.height);
}
}
}
// count nodes with inline CSS
if (node.hasAttribute('style')) {
phantomas.incrMetric('nodesWithInlineCSS');
phantomas.addOffender('nodesWithInlineCSS', phantomas.getDOMPath(node) + ' (' + node.getAttribute('style') + ')');
}
break;
case Node.TEXT_NODE:
if (whitespacesRegExp.test(node.textContent)) {
phantomas.incrMetric('whiteSpacesSize', node.textContent.length);
}
break;
}
});
phantomas.setMetric('DOMelementMaxDepth', DOMelementMaxDepth);
DOMelementMaxDepthElts.forEach(function(path) {
phantomas.addOffender('DOMelementMaxDepth', path);
});
phantomas.spyEnabled(false, 'counting iframes and images');
// count <iframe> tags
phantomas.setMetric('iframesCount', document.querySelectorAll('iframe').length); // @desc number of iframe nodes
phantomas.spyEnabled(true);
}(window.__phantomas));
});
DOMids.sort().forEach(function(id, cnt) {
if (cnt > 1) {
phantomas.incrMetric('DOMidDuplicated');
phantomas.addOffender('DOMidDuplicated', '%s: %d occurrences', id, cnt);
}
});
});
};
@@ -1,37 +0,0 @@
/**
* Analyzes if HTTP responses keep the connections alive.
*/
exports.version = '0.1';
exports.module = function(phantomas) {
'use strict';
phantomas.setMetric('closedConnections'); // @desc requests not keeping the connection alive and slowing down the next request @offenders
var closedConnectionHosts = {};
phantomas.on('recv', function(entry, res) {
var connectionHeader = (entry.headers.Connection || '').toLowerCase();
// Taking the protocol in account, in case the same domain is called with two different protocols.
var host = entry.protocol + '://' + entry.domain;
if (connectionHeader.indexOf('close') >= 0) {
// Don't blame it immediatly, wait to see if the connection is needed a second time.
closedConnectionHosts[host] = entry.url;
}
});
phantomas.on('send', function(entry, res) {
var host = entry.protocol + '://' + entry.domain;
var previousClosedConnection = closedConnectionHosts[host];
if (previousClosedConnection) {
// There was a closed connection. We can blame it safely now!
phantomas.incrMetric('closedConnections');
phantomas.addOffender('closedConnections', previousClosedConnection);
closedConnectionHosts[host] = null;
}
});
};
+1 -1
View File
@@ -1,5 +1,5 @@
var should = require('chai').should();
var phantomasWrapper = require('../../lib/tools/phantomasWrapper');
var phantomasWrapper = require('../../lib/tools/phantomas/phantomasWrapper');
describe('phantomasWrapper', function() {