Merge pull request #43 from gmetais/phantomas1.9

Phantomas1.9
This commit is contained in:
Gaël Métais
2015-01-26 20:03:28 +01:00
8 changed files with 219 additions and 61 deletions
+42
View File
@@ -148,3 +148,45 @@
font-style: italic;
font-weight: normal;
}
.colorPalette {
width: 30em;
border: 2px solid #000;
text-align: left;
/* Checkerboard background */
background-color: #ddd;
background-image: linear-gradient(45deg, #aaaaaa 25%, transparent 25%, transparent 75%, #aaaaaa 75%, #aaaaaa), linear-gradient(45deg, #aaaaaa 25%, transparent 25%, transparent 75%, #aaaaaa 75%, #aaaaaa);
background-size: 1em 1em;
background-position: 0 0, 0.5em 0.5em;
}
.colorPalette > div {
display: inline-block;
height: 2em;
position: relative;
}
.colorPalette > div div {
display: none;
position: absolute;
left: 100%;
top: 100%;
background: #FFF;
padding: 0.5em;
border: 2px solid #f1c40f;
border-radius: 0.5em;
white-space: nowrap;
z-index: 3;
font-weight: bold;
}
.colorPalette > div:hover div {
display: block;
}
.colorPalette > div:hover:after {
content: " ";
position: absolute;
background-color: inherit;
left: -0.2em;
top: -0.2em;
width: 100%;
height: 100%;
z-index: 2;
border: 0.2em solid #f1c40f;
}
+48
View File
@@ -168,4 +168,52 @@
font-weight: normal;
}
}
}
.colorPalette {
width: 30em;
border: 2px solid #000;
text-align: left;
/* Checkerboard background */
background-color: #ddd;
background-image: linear-gradient(45deg, #AAA 25%, transparent 25%, transparent 75%, #AAA 75%, #AAA), linear-gradient(45deg, #AAA 25%, transparent 25%, transparent 75%, #AAA 75%, #AAA);
background-size:1em 1em;
background-position:0 0, 0.5em 0.5em;
> div {
display: inline-block;
height: 2em;
position: relative;
div {
display: none;
position: absolute;
left: 100%;
top: 100%;
background: #FFF;
padding: 0.5em;
border: 2px solid #f1c40f;
border-radius: 0.5em;
white-space: nowrap;
z-index: 3;
font-weight: bold;
}
&:hover div {
display: block;
}
&:hover:after {
content: " ";
position: absolute;
background-color: inherit;
left: -0.2em;
top: -0.2em;
width: 100%;
height: 100%;
z-index: 2;
border: 0.2em solid #f1c40f;
}
}
}
+8 -1
View File
@@ -128,7 +128,7 @@
(<ng-pluralize count="offender.requests" when="{'one':'1 request','other':'{} requests'}"></ng-pluralize>)
</div>
<div ng-if="policyName === 'globalVariables' || policyName === 'jQueryDifferentVersions'">
<div ng-if="policyName === 'globalVariables' || policyName === 'jQueryVersionsLoaded'">
{{offender}}
</div>
@@ -143,6 +143,13 @@
<dom-tree tree="rule.offendersObj.tree"></dom-tree>
</div>
<div ng-if="policyName === 'cssColors' && rule.offendersObj.count > 0">
<p>This is the color palette, sized by total occurrences:</p>
<div class="colorPalette">
<div ng-repeat="offender in rule.offendersObj.palette" style="background-color: {{offender.color}}; width: {{offender.occurrences * 100 / rule.offendersObj.palette[0].occurrences}}%"><div>{{offender.color}} ({{offender.occurrences}} times)</div></div>
</div>
</div>
</div>
</div>
+48 -2
View File
@@ -273,7 +273,7 @@ var policies = {
"message": "<p>Current latest versions of jQuery are 1.11 (with support for old IE versions) and 2.1 (without).</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.jQueryDifferentVersions;
var differentVersions = data.toolsResults.phantomas.metrics.jQueryVersionsLoaded;
if (differentVersions === 0 || differentVersions > 1 || !data.toolsResults.phantomas.metrics.jQueryVersion) {
// Not applicable
@@ -322,7 +322,7 @@ var policies = {
}
}
},
"jQueryDifferentVersions": {
"jQueryVersionsLoaded": {
"tool": "phantomas",
"label": "Several versions loaded",
"message": "<p>jQuery is a heavy library. You should <b>never</b> load jQuery more than once on the same page.</p>",
@@ -410,6 +410,52 @@ var policies = {
};
}
},
"cssColors": {
"tool": "phantomas",
"label": "Different colors",
"message": "<p>This is the number of different colors defined in CSS.</p><p>Your CSS project will be easier to maintain if you keep a small color set.</p>",
"isOkThreshold": 30,
"isBadThreshold": 150,
"isAbnormalThreshold": 300,
"hasOffenders": true,
"offendersTransformFn": function(offenders, ruleObject) {
var deduplicatedObj = {};
offenders.map(function(offender) {
var parts = /^([^ ]*) \((\d+) times\)$/.exec(offender);
if (!parts) {
debug('cssColors offenders transform function error with "%s"', offender);
return;
}
var color = parts[1];
var count = parseInt(parts[2], 10);
deduplicatedObj[color] = (deduplicatedObj[color] || 0) + count;
});
var deduplicatedTable = [];
for (var color in deduplicatedObj) {
deduplicatedTable.push({
color: color,
occurrences: deduplicatedObj[color]
});
}
deduplicatedTable.sort(function(a, b) {
return b.occurrences - a.occurrences;
});
// Override rules.value
ruleObject.value = deduplicatedTable.length;
return {
count: deduplicatedTable.length,
palette: deduplicatedTable
};
}
},
"cssImports": {
"tool": "phantomas",
"label": "Uses of @import",
+3 -2
View File
@@ -32,7 +32,7 @@
"label": "jQuery version",
"policies": {
"jQueryVersion": 5,
"jQueryDifferentVersions": 0.1
"jQueryVersionsLoaded": 0.1
}
},
"cssSyntaxError": {
@@ -46,7 +46,8 @@
"policies": {
"cssRules": 2,
"cssComplexSelectors": 2,
"cssComplexSelectorsByAttribute": 1.5
"cssComplexSelectorsByAttribute": 1.5,
"cssColors": 0.5
}
},
"badCSS": {
+1 -1
View File
@@ -65,7 +65,7 @@ var RulesChecker = function() {
if (policy.offendersTransformFn) {
try {
offendersObj = policy.offendersTransformFn(offenders);
offendersObj = policy.offendersTransformFn(offenders, rule);
} catch(err) {
debug('Error while transforming offenders for %s', metricName);
debug(err);
@@ -7,16 +7,17 @@
/* global document: true, window: true */
/* jshint -W030 */
exports.version = '0.2.a';
exports.version = '1.0.a';
exports.module = function(phantomas) {
'use strict';
phantomas.setMetric('jQueryVersion', ''); // @desc version of jQuery framework (if loaded) [string]
phantomas.setMetric('jQueryVersionsLoaded'); // @desc number of loaded jQuery "instances" (even in the same version)
phantomas.setMetric('jQueryOnDOMReadyFunctions'); // @desc number of functions bound to onDOMReady event
phantomas.setMetric('jQueryWindowOnLoadFunctions'); // @desc number of functions bound to windowOnLoad event
phantomas.setMetric('jQuerySizzleCalls'); // @desc number of calls to Sizzle (including those that will be resolved using querySelectorAll)
phantomas.setMetric('jQuerySizzleCallsDuplicated'); // @desc number of calls on the same Sizzle request
phantomas.setMetric('jQueryDifferentVersions'); //@desc number of different jQuery versions loaded on the page (not counting iframes)
//phantomas.setMetric('jQueryEventTriggers'); // @desc number of jQuery event triggers
var jQueryFunctions = [
// DOM manipulations
@@ -110,30 +111,28 @@ exports.module = function(phantomas) {
phantomas.evaluate(function(jQueryFunctions) {
(function(phantomas) {
var jQuery;
var oldJQuery;
// TODO: create a helper - phantomas.spyGlobalVar() ?
window.__defineSetter__('jQuery', function(val) {
phantomas.spyGlobalVar('jQuery', function(jQuery) {
var version;
var jQueryFn;
var oldJQuery = jQuery;
if (!val || !val.fn) {
if (!jQuery || !jQuery.fn) {
phantomas.log('jQuery: unable to detect version!');
return;
}
version = val.fn.jquery;
jQuery = val;
jQueryFn = val.fn;
// Older jQuery (v?.?) compatibility
if (!jQueryFn) {
jQueryFn = jQuery;
// Tag the current version of jQuery to avoid multiple reports of jQuery being loaded
// when it's actually only restored via $.noConflict(true) - see comments in #435
if (jQuery.__phantomas === true) {
phantomas.log('jQuery: this instance has already been seen by phantomas');
return;
}
jQuery.__phantomas = true;
phantomas.log('jQuery: loaded v' + version);
phantomas.setMetric('jQueryVersion', version);
// report the version of jQuery
version = jQuery.fn.jquery;
phantomas.emit('jQueryLoaded', version);
phantomas.pushContext({
type: (oldJQuery) ? 'jQuery version change' : 'jQuery loaded',
callDetails: {
@@ -141,11 +140,13 @@ exports.module = function(phantomas) {
},
backtrace: phantomas.getBacktrace()
});
oldJQuery = version;
// jQuery.ready.promise
// works for jQuery 1.8.0+ (released Aug 09 2012)
phantomas.spy(val.ready, 'promise', function(func) {
phantomas.spy(jQuery.ready, 'promise', function(func) {
phantomas.incrMetric('jQueryOnDOMReadyFunctions');
phantomas.addOffender('jQueryOnDOMReadyFunctions', phantomas.getCaller(3));
phantomas.pushContext({
type: 'jQuery - onDOMReady',
@@ -160,9 +161,9 @@ exports.module = function(phantomas) {
// Sizzle calls - jQuery.find
// works for jQuery 1.3+ (released Jan 13 2009)
phantomas.spy(val, 'find', function(selector, context) {
phantomas.spy(jQuery, 'find', function(selector, context) {
phantomas.incrMetric('jQuerySizzleCalls');
phantomas.emit('onSizzleCall', selector + ' (context: ' + (phantomas.getDOMPath(context) || 'unknown') + ')');
phantomas.addOffender('jQuerySizzleCalls', '%s (in %s)', selector, (phantomas.getDOMPath(context) || 'unknown'));
phantomas.enterContext({
type: 'jQuery - find',
@@ -183,12 +184,39 @@ exports.module = function(phantomas) {
phantomas.leaveContext(moreData);
}) || phantomas.log('jQuery: can not measure jQuerySizzleCalls (jQuery used on the page is too old)!');
/*if (!jQuery.event) {
phantomas.spy(jQuery.event, 'trigger', function(ev, data, elem) {
var path = phantomas.getDOMPath(elem),
type = ev.type || ev;
phantomas.log('Event: triggered "%s" on "%s"', type, path);
phantomas.incrMetric('jQueryEventTriggers');
phantomas.addOffender('jQueryEventTriggers', '"%s" on "%s"', type, path);
});
}*/
// jQuery events bound to window' onLoad event (#451)
phantomas.spy(jQuery.fn, 'on', function(eventName, func) {
if ((eventName === 'load') && (this[0] === window)) {
phantomas.incrMetric('jQueryWindowOnLoadFunctions');
phantomas.addOffender('jQueryWindowOnLoadFunctions', phantomas.getCaller(2));
phantomas.pushContext({
type: 'jQuery - windowOnLoad',
callDetails: {
arguments: [func]
},
backtrace: phantomas.getBacktrace()
});
}
});
// Add spys on many jQuery functions
jQueryFunctions.forEach(function(functionName) {
var capitalizedName = functionName.substring(0,1).toUpperCase() + functionName.substring(1);
phantomas.spy(jQueryFn, functionName, function(args) {
phantomas.spy(jQuery.fn, functionName, function(args) {
// Clean args
args = [].slice.call(arguments);
@@ -263,42 +291,28 @@ exports.module = function(phantomas) {
phantomas.leaveContext();
}) || phantomas.log('jQuery: can not track jQuery - ' + capitalizedName + ' (this version of jQuery doesn\'t support it)');
});
});
window.__defineGetter__('jQuery', function() {
return jQuery;
});
})(window.__phantomas);
}, jQueryFunctions);
});
// count Sizzle calls to detect duplicated queries
var Collection = require('../../util/collection'),
sizzleCalls = new Collection(),
jQueryLoading = new Collection();
phantomas.on('onSizzleCall', function(request) {
sizzleCalls.push(request);
// store the last resource that was received
// try to report where given jQuery version was loaded from
phantomas.on('recv', function(entry) {
if (entry.isJS) {
lastUrl = entry.url;
}
});
phantomas.on('jQueryLoaded', function(version) {
jQueryLoading.push(version);
});
phantomas.log('jQuery: loaded v' + version);
phantomas.setMetric('jQueryVersion', version);
phantomas.on('report', function() {
sizzleCalls.sort().forEach(function(id, cnt) {
if (cnt > 1) {
phantomas.incrMetric('jQuerySizzleCallsDuplicated');
phantomas.addOffender('jQuerySizzleCallsDuplicated', '%s: %d', id, cnt);
}
});
// report multiple jQuery "instances" (issue #435)
phantomas.incrMetric('jQueryVersionsLoaded');
phantomas.addOffender('jQueryVersionsLoaded', 'v%s', version);
jQueryLoading.forEach(function(version) {
phantomas.incrMetric('jQueryDifferentVersions');
phantomas.addOffender('jQueryDifferentVersions', '%s', version);
});
phantomas.log('jQuery: v%s (probably loaded from <%s>)', version, lastUrl);
});
};
+8 -8
View File
@@ -404,20 +404,20 @@ describe('rulesChecker', function() {
results.should.deep.equals({});
// If jQueryDifferentVersions is 0
// If jQueryVersionsLoaded is 0
results = rulesChecker.check({
"toolsResults": {
"phantomas": {
"metrics": {
"jQueryVersion": "1.6.0",
"jQueryDifferentVersions": 0
"jQueryVersionsLoaded": 0
}
}
}
}, policies);
results.should.not.have.a.property('jQueryVersion');
results.should.have.a.property('jQueryDifferentVersions');
results.jQueryDifferentVersions.should.have.a.property('score').that.equals(100);
results.should.have.a.property('jQueryVersionsLoaded');
results.jQueryVersionsLoaded.should.have.a.property('score').that.equals(100);
// If there are more than 1 jQuery version
@@ -426,15 +426,15 @@ describe('rulesChecker', function() {
"phantomas": {
"metrics": {
"jQueryVersion": "1.6.0",
"jQueryDifferentVersions": 2
"jQueryVersionsLoaded": 2
}
}
}
}, policies);
results.should.not.have.a.property('jQueryVersion');
results.should.have.a.property('jQueryDifferentVersions');
results.jQueryDifferentVersions.should.have.a.property('score').that.equals(0);
results.jQueryDifferentVersions.should.have.a.property('abnormal').that.equals(true);
results.should.have.a.property('jQueryVersionsLoaded');
results.jQueryVersionsLoaded.should.have.a.property('score').that.equals(0);
results.jQueryVersionsLoaded.should.have.a.property('abnormal').that.equals(true);
});