Two new media queries related rules
This commit is contained in:
@@ -464,7 +464,7 @@ var policies = {
|
||||
"cssColors": {
|
||||
"tool": "phantomas",
|
||||
"label": "Colors count",
|
||||
"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>",
|
||||
"message": "<p>This is the number of different colors defined in CSS.</p><p>Your CSS will be easier to maintain if you keep a small color set.</p>",
|
||||
"isOkThreshold": 30,
|
||||
"isBadThreshold": 150,
|
||||
"isAbnormalThreshold": 400,
|
||||
@@ -522,6 +522,43 @@ var policies = {
|
||||
};
|
||||
}
|
||||
},
|
||||
"cssBreakpoints": {
|
||||
"tool": "mediaQueriesChecker",
|
||||
"label": "Breakpoints count",
|
||||
"message": "<p>This is the number of different breakpoints found in the stylesheets' media queries.</p><p>Please note this rule is based on <i>min-width</i>, <i>max-width</i>, <i>min-device-width</i> and <i>max-device-width</i> media queries only.</p><p>Your CSS will be easier to maintain if you keep a reasonable number of breakpoints. Try to make a fluid design - using percents - to avoid the creation of numerous breakpoints.</p>",
|
||||
"isOkThreshold": 6,
|
||||
"isBadThreshold": 40,
|
||||
"isAbnormalThreshold": 60,
|
||||
"hasOffenders": true,
|
||||
"offendersTransformFn": function(offenders) {
|
||||
var offendersTable = [];
|
||||
|
||||
for (var offender in offenders) {
|
||||
offendersTable.push({
|
||||
breakpoint: offender,
|
||||
count: offenders[offender].count,
|
||||
pixels: offenders[offender].pixels
|
||||
});
|
||||
}
|
||||
|
||||
return offendersTable;
|
||||
}
|
||||
},
|
||||
"cssMobileFirst": {
|
||||
"tool": "mediaQueriesChecker",
|
||||
"label": "Not mobile-first media queries",
|
||||
"message": "<p>This is the number of media queries that address small screens.</p><p>The common good practice, when creating a responsive website, is to write it \"mobile-first\". More explanation in <a href=\"http://www.sitepoint.com/introduction-mobile-first-media-queries\" target=\"_blank\">this great article</a>.</p>",
|
||||
"isOkThreshold": 25,
|
||||
"isBadThreshold": 200,
|
||||
"isAbnormalThreshold": 600,
|
||||
"hasOffenders": true,
|
||||
"offendersTransformFn": function(offenders) {
|
||||
return {
|
||||
count: offenders.length,
|
||||
list: offenders
|
||||
};
|
||||
}
|
||||
},
|
||||
"cssImports": {
|
||||
"tool": "phantomas",
|
||||
"label": "Uses of @import",
|
||||
|
||||
@@ -71,7 +71,9 @@
|
||||
"cssComplexSelectors": 2,
|
||||
"cssComplexSelectorsByAttribute": 1.5,
|
||||
"cssColors": 0.5,
|
||||
"similarColors": 0.5
|
||||
"similarColors": 0.5,
|
||||
"cssBreakpoints": 1,
|
||||
"cssMobileFirst": 1
|
||||
}
|
||||
},
|
||||
"badCSS": {
|
||||
|
||||
@@ -4,6 +4,7 @@ var debug = require('debug')('ylt:runner');
|
||||
var phantomasWrapper = require('./tools/phantomas/phantomasWrapper');
|
||||
var jsExecutionTransformer = require('./tools/jsExecutionTransformer');
|
||||
var colorDiff = require('./tools/colorDiff');
|
||||
var mediaQueriesChecker = require('./tools/mediaQueriesChecker');
|
||||
var weightChecker = require('./tools/weightChecker/weightChecker');
|
||||
var rulesChecker = require('./rulesChecker');
|
||||
var scoreCalculator = require('./scoreCalculator');
|
||||
@@ -32,6 +33,9 @@ var Runner = function(params) {
|
||||
// Compare colors
|
||||
data = colorDiff.compareAllColors(data);
|
||||
|
||||
// Check media queries
|
||||
data = mediaQueriesChecker.analyzeMediaQueries(data);
|
||||
|
||||
// Redownload every file
|
||||
return weightChecker.recheckAllFiles(data);
|
||||
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
var debug = require('debug')('ylt:mediaQueriesChecker');
|
||||
var parseMediaQuery = require('css-mq-parser');
|
||||
var offendersHelpers = require('../offendersHelpers');
|
||||
|
||||
|
||||
var mediaQueriesChecker = function() {
|
||||
'use strict';
|
||||
|
||||
var MOBILE_MIN_BREAKPOINT = 200;
|
||||
var MOBILE_MAX_BREAKPOINT = 300;
|
||||
|
||||
this.analyzeMediaQueries = function(data) {
|
||||
debug('Starting to check all media queries...');
|
||||
|
||||
var offenders = data.toolsResults.phantomas.offenders.cssMediaQueries;
|
||||
var mediaQueries = (offenders) ? this.parseAllMediaQueries(offenders) : [];
|
||||
|
||||
var notMobileFirstCount = 0;
|
||||
var notMobileFirstOffenders = [];
|
||||
|
||||
var breakpointsOffenders = {};
|
||||
|
||||
for (var i = 0; i < mediaQueries.length; i++) {
|
||||
var item = mediaQueries[i];
|
||||
|
||||
if (!item) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item.isForMobile) {
|
||||
notMobileFirstCount += item.mediaQuery.rules;
|
||||
notMobileFirstOffenders.push(item.mediaQuery);
|
||||
}
|
||||
|
||||
for (var j = 0; j < item.breakpoints.length; j++) {
|
||||
var breakpointString = item.breakpoints[j].string;
|
||||
if (!breakpointsOffenders[breakpointString]) {
|
||||
breakpointsOffenders[breakpointString] = {
|
||||
count: 1,
|
||||
pixels: item.breakpoints[j].pixels
|
||||
};
|
||||
} else {
|
||||
breakpointsOffenders[breakpointString].count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data.toolsResults.mediaQueriesChecker = {
|
||||
metrics: {
|
||||
cssMobileFirst: notMobileFirstCount,
|
||||
cssBreakpoints: Object.keys(breakpointsOffenders).length
|
||||
},
|
||||
offenders: {
|
||||
cssMobileFirst: notMobileFirstOffenders,
|
||||
cssBreakpoints: breakpointsOffenders
|
||||
}
|
||||
};
|
||||
|
||||
debug('End of media queries check');
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
this.parseAllMediaQueries = function(offenders) {
|
||||
return offenders.map(this.parseOneMediaQuery);
|
||||
};
|
||||
|
||||
this.parseOneMediaQuery = function(offender) {
|
||||
var splittedOffender = offendersHelpers.cssOffenderPattern(offender);
|
||||
var parts = /^@media (.*) \((\d+ rules)\)$/.exec(splittedOffender.css);
|
||||
|
||||
if (!parts) {
|
||||
debug('Failed to parse media query ' + offender);
|
||||
return false;
|
||||
}
|
||||
|
||||
var rulesCount = parseInt(parts[2], 10);
|
||||
var query = parts[1];
|
||||
|
||||
var isForMobile = false;
|
||||
var breakpoints = [];
|
||||
|
||||
try {
|
||||
|
||||
var ast = parseMediaQuery(query);
|
||||
|
||||
var min = 0;
|
||||
var max = Infinity;
|
||||
var pixels;
|
||||
|
||||
ast.forEach(function(astItem) {
|
||||
astItem.expressions.forEach(function(expression) {
|
||||
if (expression.feature === 'width' || expression.feature === 'device-width') {
|
||||
if (astItem.inverse === false) {
|
||||
if (expression.modifier === 'max') {
|
||||
pixels = toPixels(expression.value);
|
||||
max = Math.min(max, pixels);
|
||||
breakpoints.push({
|
||||
string: expression.value,
|
||||
pixels: pixels
|
||||
});
|
||||
} else if (expression.modifier === 'min') {
|
||||
pixels = toPixels(expression.value);
|
||||
min = Math.max(min, pixels);
|
||||
breakpoints.push({
|
||||
string: expression.value,
|
||||
pixels: pixels
|
||||
});
|
||||
}
|
||||
} else if (astItem.inverse === true) {
|
||||
if (expression.modifier === 'max') {
|
||||
pixels = toPixels(expression.value);
|
||||
min = Math.max(min, pixels);
|
||||
breakpoints.push({
|
||||
string: expression.value,
|
||||
pixels: pixels
|
||||
});
|
||||
} else if (expression.modifier === 'min') {
|
||||
pixels = toPixels(expression.value);
|
||||
max = Math.min(max, pixels);
|
||||
breakpoints.push({
|
||||
string: expression.value,
|
||||
pixels: pixels
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
isForMobile = (min <= MOBILE_MIN_BREAKPOINT && max >= MOBILE_MAX_BREAKPOINT && max !== Infinity);
|
||||
|
||||
} catch(error) {
|
||||
debug('Failed to parse media query ' + offender);
|
||||
}
|
||||
|
||||
return {
|
||||
mediaQuery: {
|
||||
query: query,
|
||||
rules: rulesCount,
|
||||
file: splittedOffender.file,
|
||||
line: splittedOffender.line,
|
||||
column: splittedOffender.column
|
||||
},
|
||||
isForMobile: isForMobile,
|
||||
breakpoints: breakpoints
|
||||
};
|
||||
};
|
||||
|
||||
// Parses a size in em, pt (or px) and returns it in px
|
||||
function toPixels(size) {
|
||||
var splittedSize = /^([\d\.]+)(.*)/.exec(size);
|
||||
var value = parseFloat(splittedSize[1]);
|
||||
var unit = splittedSize[2];
|
||||
|
||||
if (unit === 'em') {
|
||||
return value * 16;
|
||||
}
|
||||
|
||||
if (unit === 'pt') {
|
||||
return value / 12 * 16;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = new mediaQueriesChecker();
|
||||
Reference in New Issue
Block a user