UNPKG

canonical

Version:

Canonical code style linter and formatter for JavaScript, SCSS, CSS and JSON.

138 lines (120 loc) 6.1 kB
'use strict'; var helpers = require('../helpers'), yaml = require('js-yaml'), fs = require('fs'), path = require('path'); var colorFunctions = ['rgb', 'rgba', 'hsl', 'hsla'], cssColors = yaml.safeLoad(fs.readFileSync(path.join(__dirname, '../../data', 'literals.yml'), 'utf8')).split(' '); var getColorFunctionsCopy = function (colorFunctionsArr) { return colorFunctionsArr.slice(); }; module.exports = { 'name': 'no-color-literals', 'defaults': { 'allow-rgba': false }, 'detect': function (ast, parser) { var result = [], validColorFunctions = getColorFunctionsCopy(colorFunctions); if (parser.options['allow-rgba'] && validColorFunctions.indexOf('rgba') !== -1) { validColorFunctions.splice(validColorFunctions.indexOf('rgba'), 1); } ast.traverseByType('value', function (node, i, parent) { node.forEach(function (valElem) { var declarationType = parent.content[0].content[0].type; // check type is color, content isn't a css color literal but also it's not a variable if ((valElem.type === 'color' || cssColors.indexOf(valElem.content) !== -1) && valElem.type !== 'variable') { if (declarationType === 'ident') { result = helpers.addUnique(result, { 'ruleId': parser.rule.name, 'line': valElem.start.line, 'column': valElem.start.column, 'message': 'Color literals such as \'' + valElem.content + '\' should only be used in variable declarations', 'severity': parser.severity }); } } // if not a color value or a variable then check if it's a function else if (valElem.type === 'function') { var funcType = valElem.content[0].content; // check it's not a blacklisted color function and even if it is that it's not assigned to a variable if (validColorFunctions.indexOf(funcType) !== -1 && declarationType !== 'variable') { result = helpers.addUnique(result, { 'ruleId': parser.rule.name, 'line': valElem.start.line, 'column': valElem.start.column, 'message': 'Color functions such as \'' + funcType + '\' should only be used in variable declarations', 'severity': parser.severity }); } // if rgba usage is allowed we need to make sure only variables are being passed to it. else if (parser.options['allow-rgba'] && funcType === 'rgba' && valElem.content[1].content[0].type !== 'variable' && declarationType !== 'variable' ) { result = helpers.addUnique(result, { 'ruleId': parser.rule.name, 'line': valElem.start.line, 'column': valElem.start.column, 'message': 'A color in variable form must be passed to rgba, literals are restricted', 'severity': parser.severity }); } // if a non color function we should check it's arguments else { valElem.content.forEach( function (funcContent) { if (funcContent.type === 'arguments') { funcContent.forEach(function (funcArgs) { // if the arguments are not functions themselves if (funcArgs.type !== 'function') { // check if the argument types are therefore color literals if ((funcArgs.type === 'color' || funcArgs.type === 'ident') && (cssColors.indexOf(funcArgs.content) !== -1 || helpers.isValidHex(funcArgs.content))) { result = helpers.addUnique(result, { 'ruleId': parser.rule.name, 'line': funcArgs.start.line, 'column': funcArgs.start.column, 'message': 'Color literals such as \'' + funcArgs.content + '\' should not be passed to functions, use variables', 'severity': parser.severity }); } } // if the argument is a function itself else { // loop its arguments funcArgs.forEach( function (nestedFuncArgs) { // check again for color literals or blacklisted color functions if ( (nestedFuncArgs.type === 'color' || nestedFuncArgs.type === 'ident' || nestedFuncArgs.type === 'function') && (cssColors.indexOf(nestedFuncArgs.content) !== -1 || helpers.isValidHex(nestedFuncArgs.content) || validColorFunctions.indexOf(nestedFuncArgs.content) !== -1 ) ) { result = helpers.addUnique(result, { 'ruleId': parser.rule.name, 'line': nestedFuncArgs.start.line, 'column': nestedFuncArgs.start.column, 'message': 'Color functions such as \'' + nestedFuncArgs.content + '\' should not be passed to functions, use variables', 'severity': parser.severity }); } }); } }); } }); } } // if a map / list check to see if it's property names are the same as color literals - this is bad else if (valElem.type === 'parentheses') { valElem.traverse( function (mapVals) { if (mapVals.type === 'ident' && cssColors.indexOf(mapVals.content) !== -1 ) { result = helpers.addUnique(result, { 'ruleId': parser.rule.name, 'line': mapVals.start.line, 'column': mapVals.start.column, 'message': 'Color literals such as \'' + mapVals.content + '\' should not be used as map identifiers', 'severity': parser.severity }); } }); } }); }); return result; } };