all files / htmlcs/lib/rules/ no-meta-css.js

100% Statements 49/49
100% Branches 26/26
100% Functions 9/9
100% Lines 49/49
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108                            103×   103× 102×       30× 30×                     20×   20× 20×   20× 16×     16× 16×     16× 16×     16× 16×       20× 20×   20×           18×   18× 10×     20×                      
/**
 * @file rule: no-meta-css
 * @author chris<wfsr@foxmail.com>
 */
 
module.exports = {
 
    name: 'no-meta-css',
 
    desc: 'Classes should be semantic.',
 
    target: 'parser',
 
    lint: function (getCfg, parser, reporter) {
 
        var config = getCfg();
 
        if (!config) {
            return;
        }
 
        var MIX_VALUE_PATTERN = /[\d#>:%]+/g;
        var SPECIAL_PATTERN = /\(.+\)/g;
        var SPLITER_PATTERN = /\s*,\s*/;
 
        function reducer(map, name) {
            map[name] = true;
            return map;
        }
 
        var COLOR = 'red,green,blue,white,black,yellow,gray,silver,brown,sienna,orange,gold,ivory,indigo,purple,pink'
            .split(SPLITER_PATTERN)
            .reduce(reducer, {});
 
        var PROPERTY = 'left,right,center,middle,top,bottom,bold,width,height,size,margin,padding,clear,float'
            .split(SPLITER_PATTERN)
            .reduce(reducer, {});
 
 
        var map = {};
        var keys = {};
 
        var minLength = (config.minlen | 0) || 3;
        var threshold = (config.threshold | 0) || 3;
        function mark(name, pos) {
            var current = map[name] = map[name] || {value: .9, items: []};
 
            var value = .1;
            var item = {pos: pos};
 
            if (current.value < 1) {
                if (COLOR[name] || PROPERTY[name]) {
                    value += 1;
                }
 
                var match = name.match(MIX_VALUE_PATTERN);
                if (match) {
                    value += match.length * 1.5;
                }
 
                match = name.match(SPECIAL_PATTERN);
                if (match) {
                    value += match.length * 2;
                }
 
                var length = name.length;
                if (length <= minLength) {
                    value += 1 + (minLength - length) * .5;
                }
            }
 
            current.value += value;
            current.items.push(item);
 
            if (!keys[name] && current.value > threshold) {
                keys[name] = true;
            }
        }
 
        function report(name, item) {
            reporter.warn(item.pos, '045', 'Class name(' + name + ') should be semantic.');
        }
 
        parser.tokenizer.on('attribdata', function (value) {
            var name = parser._attribname.toLowerCase();
 
            if (name !== 'class') {
                return;
            }
 
            var pos = this._sectionStart;
            value.split(/\s+/).forEach(function (name) {
                mark(name, pos, value);
            });
 
        });
 
        parser.on('end', function () {
            Object.keys(keys).forEach(function (key) {
                map[key].items.forEach(function (item) {
                    report(key, item);
                });
            });
        });
    }
 
};