'use strict'; var helpers = require('../helpers'); var shortVals = [ 'border-color', 'border-radius', 'border-style', 'border-width', 'margin', 'padding' ]; /** * Checks to see if a series of values can be condensed down to a singular value * * @param {array} value - The array of values to check * @param {array} allowed - The parser options to specify the levels allowed to condense to * @returns {boolean} Whether the values can be condensed to a singular value */ var condenseToOne = function (value, allowed) { if (allowed.indexOf(1) !== -1 && value.length > 1) { for (var i = 1; i < value.length; i++) { if (value[i] !== value[0]) { return false; } } return true; } return false; }; /** * Checks to see if a series of values can be condensed down to two values * * @param {array} value - The array of values to check * @param {array} allowed - The parser options to specify the levels allowed to condense to * @returns {boolean} Whether the values can be condensed to two values */ var condenseToTwo = function (value, allowed) { if (allowed.indexOf(2) !== -1 && value.length > 2) { if ((value[0] === value[2] && value[1] === value[3]) || (value[0] === value[2] && !value[3] && value[0] !== value[1])) { return true; } } return false; }; /** * Checks to see if a series of values can be condensed down to three values * * @param {array} value - The array of values to check * @param {array} allowed - The parser options to specify the levels allowed to condense to * @returns {boolean} Whether the values can be condensed to three values */ var condenseToThree = function (value, allowed) { if (allowed.indexOf(3) !== -1 && value.length > 3) { if (value[1] === value[3] ) { return true; } } return false; }; /** * Used to scan property values and create a string representation of the values to display * * @param {Object} node - The current node * @returns {string} A string reconstruction of the current properties value */ var scanValue = function (node) { var curValue = []; var fullVal = ''; node.forEach(function (val) { // add to our value string depending on node type if (val.is('dimension')) { val.forEach(function (el) { fullVal += el.content; }); } else if (val.is('percentage')) { val.forEach(function (el) { fullVal += el.content + '%'; }); } else if (val.is('interpolation')) { fullVal += '#{' + scanValue(val.content) + '}'; } else if (val.is('color')) { fullVal += '#' + val.content + ''; } else if ( val.is('operator') || val.is('ident') || val.is('number') || val.is('unaryOperator') || val.is('string') ) { fullVal += val.content; } else if (val.is('variable')) { val.forEach(function (el) { fullVal += '$' + el.content; }); } else if (val.is('function')) { var func = val.first('ident'), args = ''; val.forEach('arguments', function (arg) { args = scanValue(arg).join(' '); }); fullVal = func + '(' + args + ')'; } else if (val.is('parentheses')) { fullVal += '(' + scanValue(val).join(' ') + ')'; } else if (val.is('space')) { // This is a non value character such as a space // We want to start another value here curValue.push(fullVal); // reset the value string for the next iteration fullVal = ''; } }); if (fullVal !== '') { // The last dimension in a value will not be followed by a character so we push here curValue.push(fullVal); } return curValue; }; module.exports = { 'name': 'shorthand-values', 'defaults': { 'allowed-shorthands': [1, 2, 3] }, 'detect': function (ast, parser) { var result = []; ast.traverseByType('declaration', function (declaration) { var isShorthandProperty = false, property; declaration.traverse(function (item) { if (item.is('property')) { item.traverse(function (child) { // check if the property is a possible shorthand property if (shortVals.indexOf(child.content) !== -1) { isShorthandProperty = true; // store a reference to the property for our error property = shortVals[shortVals.indexOf(child.content)]; } }); } if (isShorthandProperty) { var value = []; if (item.is('value')) { var node = item.content; // Build each value into an array of strings with value and type value = scanValue(node); if (value.length <= 4 && value.length >= 1) { var output = []; // check which values can condense if (condenseToOne(value, parser.options['allowed-shorthands'])) { output = [value[0]]; } else if (condenseToTwo(value, parser.options['allowed-shorthands'])) { output = [value[0], value[1]]; } else if (condenseToThree(value, parser.options['allowed-shorthands'])) { output = [value[0], value[1], value[2]]; } if (output.length) { result = helpers.addUnique(result, { 'ruleId': parser.rule.name, 'line': item.start.line, 'column': item.start.column, 'message': 'Property `' + property + '` should be written more concisely as `' + output.join(' ') + '` instead of `' + value.join(' ') + '`', 'severity': parser.severity }); } } } } }); }); return result; } };