浏览代码

End of the offender transform functions

Gaël Métais 10 年之前
父节点
当前提交
4a8a5829d7
共有 3 个文件被更改,包括 235 次插入24 次删除
  1. 207 24
      lib/metadata/policies.js
  2. 15 0
      lib/offendersHelpers.js
  3. 13 0
      test/core/offendersHelpersTest.js

+ 207 - 24
lib/metadata/policies.js

@@ -510,7 +510,7 @@ var policies = {
             return offenders.map(function(offender) {
             return offenders.map(function(offender) {
                 var splittedOffender = offendersHelpers.cssOffenderPattern(offender);
                 var splittedOffender = offendersHelpers.cssOffenderPattern(offender);
 
 
-                var parts = /^([^{]*)(?: { ([^ ]+): (.*) }) \/\/ (.*)$/.exec(splittedOffender.offender);
+                var parts = /^([^{]*)(?: ?{ ?([^ ]+): (.*) ?}) \/\/ (.*)$/.exec(splittedOffender.offender);
 
 
                 if (!parts) {
                 if (!parts) {
                     debug('cssOldPropertyPrefixes offenders transform function error with "%s"', offender);
                     debug('cssOldPropertyPrefixes offenders transform function error with "%s"', offender);
@@ -535,7 +535,17 @@ var policies = {
         "message": "<p>This is one way to remove complexity from a CSS rule. Generally, when \"body\" is specified in a rule it can be removed, because an element is necessarily inside the body.</p>",
         "message": "<p>This is one way to remove complexity from a CSS rule. Generally, when \"body\" is specified in a rule it can be removed, because an element is necessarily inside the body.</p>",
         "isOkThreshold": 0,
         "isOkThreshold": 0,
         "isBadThreshold": 50,
         "isBadThreshold": 50,
-        "isAbnormalThreshold": 200
+        "isAbnormalThreshold": 200,
+        "offendersTransformFn": function(offenders) {
+            return offenders.map(function(offender) {
+                var splittedOffender = offendersHelpers.cssOffenderPattern(offender);
+
+                var rule = splittedOffender.offender;
+                rule = rule.replace(/body/, '<b>body</b>');
+
+                return rule + ' @ ' + splittedOffender.line + ':' + splittedOffender.character;
+            });
+        }
     },
     },
     "cssRedundantChildNodesSelectors": {
     "cssRedundantChildNodesSelectors": {
         "tool": "phantomas",
         "tool": "phantomas",
@@ -543,7 +553,27 @@ var policies = {
         "message": "<p>Some tags included inside other tags are obvious. For example, when \"ul li\" is specified in a rule, \"ul\" can be removed because the \"li\" element is <b>always</b> inside a \"ul\". Same thing for \"tr td\", \"select option\", ...</p><p>Lowering compexity in CSS selectors can make the page load a little faster.</p>",
         "message": "<p>Some tags included inside other tags are obvious. For example, when \"ul li\" is specified in a rule, \"ul\" can be removed because the \"li\" element is <b>always</b> inside a \"ul\". Same thing for \"tr td\", \"select option\", ...</p><p>Lowering compexity in CSS selectors can make the page load a little faster.</p>",
         "isOkThreshold": 0,
         "isOkThreshold": 0,
         "isBadThreshold": 50,
         "isBadThreshold": 50,
-        "isAbnormalThreshold": 200
+        "isAbnormalThreshold": 200,
+        "offendersTransformFn": function(offenders) {
+            return offenders.map(function(offender) {
+                var splittedOffender = offendersHelpers.cssOffenderPattern(offender);
+
+                var rule = splittedOffender.offender;
+                var redundanters = [
+                    ['ul', 'li'],
+                    ['ol', 'li'],
+                    ['select', 'option'],
+                    ['table', 'tr'],
+                    ['table', 'th'],
+                ];
+
+                redundanters.forEach(function(couple) {
+                    rule = rule.replace(new RegExp('(^| |>)' + couple[0] + '([^ >]*)?([ >]| > )' + couple[1] + '([^\\w-]|$)', 'g'), '$1<b>' + couple[0] + '</b>$2$3<b>' + couple[1] + '</b>$4');
+                });
+
+                return rule + ' @ ' + splittedOffender.line + ':' + splittedOffender.character;
+            });
+        }
     },
     },
     "requests": {
     "requests": {
         "tool": "phantomas",
         "tool": "phantomas",
@@ -552,7 +582,17 @@ var policies = {
         "isOkThreshold": 15,
         "isOkThreshold": 15,
         "isBadThreshold": 100,
         "isBadThreshold": 100,
         "isAbnormalThreshold": 200,
         "isAbnormalThreshold": 200,
-        "takeOffendersFrom": ["htmlCount", "jsCount", "cssCount", "imageCount", "webfontCount", "videoCount", "jsonCount", "jsonCount"]
+        "takeOffendersFrom": ["htmlCount", "jsCount", "cssCount", "imageCount", "webfontCount", "videoCount", "jsonCount", "jsonCount"],
+        "offendersTransformFn": function(offenders) {
+            return offenders
+                .map(function(offender) {
+                    return offendersHelpers.fileWithSizePattern(offender);
+                }).sort(function(a, b) {
+                    return b.size - a.size;
+                }).map(function(fileObj) {
+                    return offendersHelpers.urlToLink(fileObj.file) + ((fileObj.size !== undefined) ? ' (' + fileObj.size + ' kB)' : '');
+                });
+        }
     },
     },
     "htmlCount": {
     "htmlCount": {
         "tool": "phantomas",
         "tool": "phantomas",
@@ -560,7 +600,13 @@ var policies = {
         "message": "<p>The number of HTML pages requests, HTML fragments or iframes.</p>",
         "message": "<p>The number of HTML pages requests, HTML fragments or iframes.</p>",
         "isOkThreshold": 10,
         "isOkThreshold": 10,
         "isBadThreshold": 20,
         "isBadThreshold": 20,
-        "isAbnormalThreshold": 30
+        "isAbnormalThreshold": 30,
+        "offendersTransformFn": function(offenders) {
+            return offenders.map(function(offender) {
+                var fileObj = offendersHelpers.fileWithSizePattern(offender);
+                return offendersHelpers.urlToLink(fileObj.file) + ((fileObj.size !== undefined) ? ' (' + fileObj.size + ' kB)' : '');
+            });
+        }
     },
     },
     "jsCount": {
     "jsCount": {
         "tool": "phantomas",
         "tool": "phantomas",
@@ -568,7 +614,13 @@ var policies = {
         "message": "<p>Reduce the number of scripts by concatenating them.</p>",
         "message": "<p>Reduce the number of scripts by concatenating them.</p>",
         "isOkThreshold": 5,
         "isOkThreshold": 5,
         "isBadThreshold": 15,
         "isBadThreshold": 15,
-        "isAbnormalThreshold": 30
+        "isAbnormalThreshold": 30,
+        "offendersTransformFn": function(offenders) {
+            return offenders.map(function(offender) {
+                var fileObj = offendersHelpers.fileWithSizePattern(offender);
+                return offendersHelpers.urlToLink(fileObj.file) + ((fileObj.size !== undefined) ? ' (' + fileObj.size + ' kB)' : '');
+            });
+        }
     },
     },
     "cssCount": {
     "cssCount": {
         "tool": "phantomas",
         "tool": "phantomas",
@@ -576,7 +628,13 @@ var policies = {
         "message": "<p>Reduce the number of stylesheets by concatenating them.</p>",
         "message": "<p>Reduce the number of stylesheets by concatenating them.</p>",
         "isOkThreshold": 3,
         "isOkThreshold": 3,
         "isBadThreshold": 10,
         "isBadThreshold": 10,
-        "isAbnormalThreshold": 22
+        "isAbnormalThreshold": 22,
+        "offendersTransformFn": function(offenders) {
+            return offenders.map(function(offender) {
+                var fileObj = offendersHelpers.fileWithSizePattern(offender);
+                return offendersHelpers.urlToLink(fileObj.file) + ((fileObj.size !== undefined) ? ' (' + fileObj.size + ' kB)' : '');
+            });
+        }
     },
     },
     "imageCount": {
     "imageCount": {
         "tool": "phantomas",
         "tool": "phantomas",
@@ -584,7 +642,13 @@ var policies = {
         "message": "<p>Reduce the number of images by lazyloading them, by spriting them or by creating an icons font.</p>",
         "message": "<p>Reduce the number of images by lazyloading them, by spriting them or by creating an icons font.</p>",
         "isOkThreshold": 15,
         "isOkThreshold": 15,
         "isBadThreshold": 40,
         "isBadThreshold": 40,
-        "isAbnormalThreshold": 70
+        "isAbnormalThreshold": 70,
+        "offendersTransformFn": function(offenders) {
+            return offenders.map(function(offender) {
+                var fileObj = offendersHelpers.fileWithSizePattern(offender);
+                return offendersHelpers.urlToLink(fileObj.file) + ((fileObj.size !== undefined) ? ' (' + fileObj.size + ' kB)' : '');
+            });
+        }
     },
     },
     "webfontCount": {
     "webfontCount": {
         "tool": "phantomas",
         "tool": "phantomas",
@@ -592,15 +656,27 @@ var policies = {
         "message": "<p>Fonts are loaded on the critical path of the head. Load as many as possible.</p>",
         "message": "<p>Fonts are loaded on the critical path of the head. Load as many as possible.</p>",
         "isOkThreshold": 0,
         "isOkThreshold": 0,
         "isBadThreshold": 3,
         "isBadThreshold": 3,
-        "isAbnormalThreshold": 5
+        "isAbnormalThreshold": 5,
+        "offendersTransformFn": function(offenders) {
+            return offenders.map(function(offender) {
+                var fileObj = offendersHelpers.fileWithSizePattern(offender);
+                return offendersHelpers.urlToLink(fileObj.file) + ((fileObj.size !== undefined) ? ' (' + fileObj.size + ' kB)' : '');
+            });
+        }
     },
     },
     "videoCount": {
     "videoCount": {
         "tool": "phantomas",
         "tool": "phantomas",
-        "label": "Videos count",
+        "label": "Video count",
         "message": "<p>The number of videos loaded.</p>",
         "message": "<p>The number of videos loaded.</p>",
         "isOkThreshold": 1,
         "isOkThreshold": 1,
         "isBadThreshold": 5,
         "isBadThreshold": 5,
-        "isAbnormalThreshold": 15
+        "isAbnormalThreshold": 15,
+        "offendersTransformFn": function(offenders) {
+            return offenders.map(function(offender) {
+                var fileObj = offendersHelpers.fileWithSizePattern(offender);
+                return offendersHelpers.urlToLink(fileObj.file) + ((fileObj.size !== undefined) ? ' (' + fileObj.size + ' kB)' : '');
+            });
+        }
     },
     },
     "jsonCount": {
     "jsonCount": {
         "tool": "phantomas",
         "tool": "phantomas",
@@ -608,7 +684,13 @@ var policies = {
         "message": "<p>The number of AJAX requests to JSON files or webservices.</p>",
         "message": "<p>The number of AJAX requests to JSON files or webservices.</p>",
         "isOkThreshold": 2,
         "isOkThreshold": 2,
         "isBadThreshold": 10,
         "isBadThreshold": 10,
-        "isAbnormalThreshold": 25
+        "isAbnormalThreshold": 25,
+        "offendersTransformFn": function(offenders) {
+            return offenders.map(function(offender) {
+                var fileObj = offendersHelpers.fileWithSizePattern(offender);
+                return offendersHelpers.urlToLink(fileObj.file) + ((fileObj.size !== undefined) ? ' (' + fileObj.size + ' kB)' : '');
+            });
+        }
     },
     },
     "otherCount": {
     "otherCount": {
         "tool": "phantomas",
         "tool": "phantomas",
@@ -616,7 +698,13 @@ var policies = {
         "message": "<p>They can be Flash, XML, music or any unknown format.</p>",
         "message": "<p>They can be Flash, XML, music or any unknown format.</p>",
         "isOkThreshold": 5,
         "isOkThreshold": 5,
         "isBadThreshold": 20,
         "isBadThreshold": 20,
-        "isAbnormalThreshold": 40
+        "isAbnormalThreshold": 40,
+        "offendersTransformFn": function(offenders) {
+            return offenders.map(function(offender) {
+                var fileObj = offendersHelpers.fileWithSizePattern(offender);
+                return offendersHelpers.urlToLink(fileObj.file) + ((fileObj.size !== undefined) ? ' (' + fileObj.size + ' kB)' : '');
+            });
+        }
     },
     },
     "smallJsFiles": {
     "smallJsFiles": {
         "tool": "phantomas",
         "tool": "phantomas",
@@ -624,7 +712,13 @@ var policies = {
         "message": "<p>Number of JS assets smaller than 2 KB that could probably be inlined or merged.</p>",
         "message": "<p>Number of JS assets smaller than 2 KB that could probably be inlined or merged.</p>",
         "isOkThreshold": 2,
         "isOkThreshold": 2,
         "isBadThreshold": 10,
         "isBadThreshold": 10,
-        "isAbnormalThreshold": 16
+        "isAbnormalThreshold": 16,
+        "offendersTransformFn": function(offenders) {
+            return offenders.map(function(offender) {
+                var fileObj = offendersHelpers.fileWithSizePattern(offender);
+                return offendersHelpers.urlToLink(fileObj.file) + ((fileObj.size !== undefined) ? ' (' + fileObj.size + ' kB)' : '');
+            });
+        }
     },
     },
     "smallCssFiles": {
     "smallCssFiles": {
         "tool": "phantomas",
         "tool": "phantomas",
@@ -632,7 +726,13 @@ var policies = {
         "message": "<p>Number of CSS assets smaller than 2 KB that could probably be inlined or merged.</p>",
         "message": "<p>Number of CSS assets smaller than 2 KB that could probably be inlined or merged.</p>",
         "isOkThreshold": 0,
         "isOkThreshold": 0,
         "isBadThreshold": 8,
         "isBadThreshold": 8,
-        "isAbnormalThreshold": 12
+        "isAbnormalThreshold": 12,
+        "offendersTransformFn": function(offenders) {
+            return offenders.map(function(offender) {
+                var fileObj = offendersHelpers.fileWithSizePattern(offender);
+                return offendersHelpers.urlToLink(fileObj.file) + ((fileObj.size !== undefined) ? ' (' + fileObj.size + ' kB)' : '');
+            });
+        }
     },
     },
     "smallImages": {
     "smallImages": {
         "tool": "phantomas",
         "tool": "phantomas",
@@ -640,7 +740,13 @@ var policies = {
         "message": "<p>Images smaller than 2 KB that could be base64 encoded or merged into a sprite.</p>",
         "message": "<p>Images smaller than 2 KB that could be base64 encoded or merged into a sprite.</p>",
         "isOkThreshold": 2,
         "isOkThreshold": 2,
         "isBadThreshold": 17,
         "isBadThreshold": 17,
-        "isAbnormalThreshold": 30
+        "isAbnormalThreshold": 30,
+        "offendersTransformFn": function(offenders) {
+            return offenders.map(function(offender) {
+                var fileObj = offendersHelpers.fileWithSizePattern(offender);
+                return offendersHelpers.urlToLink(fileObj.file) + ((fileObj.size !== undefined) ? ' (' + fileObj.size + ' kB)' : '');
+            });
+        }
     },
     },
     "notFound": {
     "notFound": {
         "tool": "phantomas",
         "tool": "phantomas",
@@ -648,7 +754,12 @@ var policies = {
         "message": "<p>404 errors are never cached, so each time a page ask for it, it hits se server. Even if it is behind a CDN or a reverse-proxy cache.</p>",
         "message": "<p>404 errors are never cached, so each time a page ask for it, it hits se server. Even if it is behind a CDN or a reverse-proxy cache.</p>",
         "isOkThreshold": 0,
         "isOkThreshold": 0,
         "isBadThreshold": 1,
         "isBadThreshold": 1,
-        "isAbnormalThreshold": 1
+        "isAbnormalThreshold": 1,
+        "offendersTransformFn": function(offenders) {
+            return offenders.map(function(offender) {
+                return offendersHelpers.urlToLink(offender);
+            });
+        }
     },
     },
     "closedConnections": {
     "closedConnections": {
         "tool": "phantomas",
         "tool": "phantomas",
@@ -656,15 +767,25 @@ var policies = {
         "message": "<p>This counts the number of requests not keeping the connection alive (specifying \"Connection: close\" in the response headers). It is only counting a request if it is followed by another request on the same domain.</p><p>This is slowing down the next request, because the brower needs to open a new connection to the server, which means a additional round-trip.</p><p>Correct the problem by setting a Keep-Alive header on the guilty server.</p>",
         "message": "<p>This counts the number of requests not keeping the connection alive (specifying \"Connection: close\" in the response headers). It is only counting a request if it is followed by another request on the same domain.</p><p>This is slowing down the next request, because the brower needs to open a new connection to the server, which means a additional round-trip.</p><p>Correct the problem by setting a Keep-Alive header on the guilty server.</p>",
         "isOkThreshold": 0,
         "isOkThreshold": 0,
         "isBadThreshold": 8,
         "isBadThreshold": 8,
-        "isAbnormalThreshold": 20
+        "isAbnormalThreshold": 20,
+        "offendersTransformFn": function(offenders) {
+            return offenders.map(function(offender) {
+                return offendersHelpers.urlToLink(offender);
+            });
+        }
     },
     },
     "multipleRequests": {
     "multipleRequests": {
         "tool": "phantomas",
         "tool": "phantomas",
         "label": "Duplicated requests",
         "label": "Duplicated requests",
         "message": "<p>This only happens when the asset has no cache and is requested more than once on the same page. Be very careful about it.</p>",
         "message": "<p>This only happens when the asset has no cache and is requested more than once on the same page. Be very careful about it.</p>",
         "isOkThreshold": 0,
         "isOkThreshold": 0,
-        "isBadThreshold": 5,
-        "isAbnormalThreshold": 10
+        "isBadThreshold": 3,
+        "isAbnormalThreshold": 10,
+        "offendersTransformFn": function(offenders) {
+            return offenders.map(function(offender) {
+                return offendersHelpers.urlToLink(offender);
+            });
+        }
     },
     },
     "cachingDisabled": {
     "cachingDisabled": {
         "tool": "phantomas",
         "tool": "phantomas",
@@ -672,7 +793,12 @@ var policies = {
         "message": "<p>Counts responses with caching disabled (max-age=0)</p><p>Fix immediatly if on static assets.</p>",
         "message": "<p>Counts responses with caching disabled (max-age=0)</p><p>Fix immediatly if on static assets.</p>",
         "isOkThreshold": 0,
         "isOkThreshold": 0,
         "isBadThreshold": 12,
         "isBadThreshold": 12,
-        "isAbnormalThreshold": 25
+        "isAbnormalThreshold": 25,
+        "offendersTransformFn": function(offenders) {
+            return offenders.map(function(offender) {
+                return offendersHelpers.urlToLink(offender);
+            });
+        }
     },
     },
     "cachingNotSpecified": {
     "cachingNotSpecified": {
         "tool": "phantomas",
         "tool": "phantomas",
@@ -680,7 +806,12 @@ var policies = {
         "message": "<p>When no caching is specified, each browser will handle it differently. Most of the time, it will automatically add a cache for you, but a poor one. You'd better handle it yourself.</p>",
         "message": "<p>When no caching is specified, each browser will handle it differently. Most of the time, it will automatically add a cache for you, but a poor one. You'd better handle it yourself.</p>",
         "isOkThreshold": 5,
         "isOkThreshold": 5,
         "isBadThreshold": 20,
         "isBadThreshold": 20,
-        "isAbnormalThreshold": 40
+        "isAbnormalThreshold": 40,
+        "offendersTransformFn": function(offenders) {
+            return offenders.map(function(offender) {
+                return offendersHelpers.urlToLink(offender);
+            });
+        }
     },
     },
     "cachingTooShort": {
     "cachingTooShort": {
         "tool": "phantomas",
         "tool": "phantomas",
@@ -688,7 +819,47 @@ var policies = {
         "message": "<p>Responses with too short caching time (less than a week).</p><p>The longer you cache, the better. Add versionning to your static assets, if it's not already done, and set their cache time to one year.</p>",
         "message": "<p>Responses with too short caching time (less than a week).</p><p>The longer you cache, the better. Add versionning to your static assets, if it's not already done, and set their cache time to one year.</p>",
         "isOkThreshold": 5,
         "isOkThreshold": 5,
         "isBadThreshold": 20,
         "isBadThreshold": 20,
-        "isAbnormalThreshold": 40
+        "isAbnormalThreshold": 40,
+        "offendersTransformFn": function(offenders) {
+            return offenders
+                .map(function(offender) {
+                    var parts = /^([^ ]*) cached for (-?\d+(\.\d+)?) s$/.exec(offender);
+
+                    if (!parts) {
+                        debug('cachingTooShort offenders transform function error with "%s"', offender);
+                        return {
+                            file: offender
+                        };
+                    }
+
+                    return {
+                        file: parts[1],
+                        ttl: Math.round(parseFloat(parts[2]))
+                    };
+                }).sort(function(a, b) {
+                    return a.ttl - b.ttl;
+                }).map(function(obj) {
+                    var duration = obj.ttl;
+                    var unit = 'seconds';
+
+                    if (duration >= 120) {
+                        duration = Math.round(duration / 60);
+                        unit = 'minutes';
+                    }
+
+                    if (duration >= 120) {
+                        duration = Math.round(duration / 60);
+                        unit = 'hours';
+                    }
+
+                    if (duration >= 48) {
+                        duration = Math.round(duration / 24);
+                        unit = 'days';
+                    }
+
+                    return offendersHelpers.urlToLink(obj.file) + ' cached for <b>' + duration + ' ' + unit + '</b>';
+                });
+        }
     },
     },
     "domains": {
     "domains": {
         "tool": "phantomas",
         "tool": "phantomas",
@@ -696,7 +867,19 @@ var policies = {
         "message": "<p>For each domain met, the browser needs to make a DNS look-up, which is slow. Avoid having to many different domains and the page should render faster.</p><p>By the way, domain sharding is not a good practice anymore.</p>",
         "message": "<p>For each domain met, the browser needs to make a DNS look-up, which is slow. Avoid having to many different domains and the page should render faster.</p><p>By the way, domain sharding is not a good practice anymore.</p>",
         "isOkThreshold": 10,
         "isOkThreshold": 10,
         "isBadThreshold": 25,
         "isBadThreshold": 25,
-        "isAbnormalThreshold": 50
+        "isAbnormalThreshold": 50,
+        "offendersTransformFn": function(offenders) {
+            return offenders.map(function(offender) {
+                var parts = /^([^ ]*): (\d+) request\(s\)$/.exec(offender);
+
+                if (!parts) {
+                    debug('domains offenders transform function error with "%s"', offender);
+                    return offender;
+                }
+
+                return '<b>' + parts[1] + '</b> (' + parts[2] + ' ' + ((parseInt(parts[2], 10) > 1) ? 'requests' : 'request') + ')';
+            });
+        }
     }
     }
 };
 };
 
 

+ 15 - 0
lib/offendersHelpers.js

@@ -172,6 +172,21 @@ var OffendersHelpers = function() {
         }
         }
     };
     };
 
 
+    this.fileWithSizePattern = function(fileWithSize) {
+        var parts = /^([^ ]*) \((\d+\.\d{2}) kB\)$/.exec(fileWithSize);
+
+        if (!parts) {
+            return {
+                file: fileWithSize
+            };
+        } else {
+            return {
+                file: parts[1],
+                size: parseFloat(parts[2])
+            };
+        }
+    };
+
 };
 };
 
 
 module.exports = new OffendersHelpers();
 module.exports = new OffendersHelpers();

+ 13 - 0
test/core/offendersHelpersTest.js

@@ -267,4 +267,17 @@ describe('offendersHelpers', function() {
 
 
     });
     });
 
 
+    describe('fileWithSizePattern', function() {
+
+        it('should return an object', function() {
+            var result = offendersHelpers.fileWithSizePattern('http://img3.pouet.com/2008/portail/js/jq-timer.js (1.72 kB)');
+
+            result.should.deep.equal({
+                file: 'http://img3.pouet.com/2008/portail/js/jq-timer.js',
+                size: 1.72
+            });
+        });
+
+    });
+
 });
 });