UNPKG

echo-fecs

Version:

Front End Code Style Suite

490 lines (398 loc) 13.1 kB
/** * @file baidu reporter * @author chris<wfsr@foxmail.com> */ var util = require('../../util'); /** * 校验规则与对应语言规范的映射 * * @namespace */ var maps = {}; // 读取当前目录下的 json 文件,以文件名为 key // json 配置文件中可以使用 javascript 注释 util.readConfigs(__dirname, maps); if (maps.esNext) { util.extend(maps.javascript, maps.esNext); } /** * 用于匹配错误信息最后的百度规范编号 * * @const * @type {RegExp} */ var BAIDU_CODE_REG = /baidu(\d{3})$/; /** * 规则编号最大长度 * * @const * @type {number} */ var RULE_CODE_MAX_WIDTH = 3; /** * 错误等级 * * @enum {number} */ var Severity = { WARN: 1, ERROR: 2 }; /** * baidu 自定义 reporter 针对各校验器 error 对象的处理 * * @namespace */ var baidu = { /** * 对于 eslint rule 匹配多条规范时的区分处理函数集 * * @type {Object} */ eslintRules: { 'fecs-indent': function (error) { var match = error.message.match(BAIDU_CODE_REG); return match ? match[1] : '003'; }, 'fecs-valid-jsdoc': function (error) { var match = error.message.match(BAIDU_CODE_REG); return match ? match[1] : '999'; }, 'fecs-valid-var-jsdoc': function (error) { var match = error.message.match(BAIDU_CODE_REG); return match ? match[1] : '999'; }, 'fecs-valid-class-jsdoc': function (error) { var match = error.message.match(BAIDU_CODE_REG); return match ? match[1] : '999'; }, 'fecs-valid-constructor': function (error) { var match = error.message.match(BAIDU_CODE_REG); return match ? match[1] : '999'; }, 'fecs-esnext-ext': function (error) { var match = error.message.match(BAIDU_CODE_REG); return match ? match[1] : '999'; }, 'fecs-camelcase': function (error) { var match = error.message.match(BAIDU_CODE_REG); return match ? match[1] : '999'; }, 'fecs-properties-quote': function (error) { var match = error.message.match(BAIDU_CODE_REG); return match ? match[1] : '999'; }, 'spaced-comment': function (error) { if (error.origin.nodeType === 'Block') { if (/^\/\*(eslint|jshint|jslint|istanbul|globals?)\b/.test(error.origin.source)) { return '999'; } return '039'; } return '038'; }, 'space-before-function-paren': function (error) { var message = error.message; return ~message.indexOf('Missing space') ? '007' : '009'; }, 'no-undef': function (error) { var isReadOnly = error.message.match(/is read only\.$/); return isReadOnly ? '997' : '070'; }, 'babel/new-cap': function (error) { var origin = error.origin; var chr = origin.source.charAt(origin.column - 1); if (chr === '"' || chr === '\'') { error.column += 1; chr = origin.source.charAt(origin.column); } if (/[A-Z]/.test(chr)) { return /[\.\[]/.test(origin.source.charAt(origin.column - 2)) ? '030' : '027'; } return '029'; }, 'fecs-no-extra-semi': function (error) { var origin = error.origin; return { MethodDefinition: '606', ClassDeclaration: '605', FunctionDeclaration: '023', ExportDefaultDeclaration: '607', ExportNamedDeclaration: '607', Decorator: '608' }[origin.nodeType] || '997'; }, 'fecs-arrow-body-style': function (error) { return ~error.message.indexOf('Expected') ? '611' : '610'; }, 'fecs-use-method-definition': function (error) { var origin = error.origin; return { FunctionExpression: '624', ArrowFunctionExpression: '626' }[origin.nodeType]; }, 'no-extend-native': function (error) { return error.message.indexOf('Promise prototype') === 0 ? '643' : '096'; }, 'fecs-use-for-of': function (error) { return ~error.message.indexOf('for-of') ? '635' : '625'; }, 'fecs-prefer-spread-element': function (error) { var origin = error.origin; return { CallExpression: '633', SpreadElement: '634' }[origin.nodeType]; }, 'fecs-prefer-async-await': function (error) { var origin = error.origin; return { CallExpression: '645', YieldExpression: '644' }[origin.nodeType]; }, 'fecs-valid-map-set': function (error) { var origin = error.origin; return { AssignmentExpression: '636', ObjectExpression: '637', ForInStatement: '638', UnaryExpression: '639' }[origin.nodeType]; }, 'fecs-valid-dom-style': function (error) { var origin = error.origin; return origin.nodeType === 'Literal' ? '138' : '137'; }, 'fecs-no-require': function (error) { var origin = error.origin; return origin.nodeType === 'CallExpression' ? '655' : '998'; } }, /** * 针对 eslint 校验错误的处理 * * @param {!Object} error eslint 的错误对象 * @param {!Object} level 错误的等级数据 * @return {string} 对应的规则编码入描述信息 */ eslint: function (error, level) { var code; if (baidu.eslintRules[error.rule]) { code = baidu.eslintRules[error.rule](error); // ignore error with empty code if (!code) { return ''; } } else { code = maps.eslintMap[error.rule] || error.code; } var rule = maps.javascript[code]; var desc = error.message.replace(BAIDU_CODE_REG, ''); if (rule) { level.warn = rule.level < 2 ? 1 : 0; desc = rule.desc; } else { code = '999'; } return ' JS' + code + ': ' + desc; }, /** * 对于 csshint rule 匹配多条规范时的区分处理函数集 * * @type {Object} */ csshintRules: { 'require-after-space': function (error) { return error.origin.errorChar === ':' ? '004' : '005'; }, 'require-newline': function (error) { var map = { 'selector': '008', 'property': '011', 'media-query-condition': '044' }; return map[error.origin.errorChar]; }, 'require-doublequotes': function (error) { return error.origin.errorChar === 'attr-selector' ? '010' : '024'; }, 'require-number': function (error) { return error.origin.errorChar === 'font-weight' ? '039' : '040'; }, 'shorthand': function (error) { return error.origin.errorChar === 'color' ? '030' : '015'; } }, lesslintRules: {}, htmlcsRules: {}, /** * 针对 csshint 校验错误的处理 * * @param {!Object} error csshint 的错误对象 * @param {!Object} level 错误的等级数据 * @return {string} 对应的规则编码入描述信息 */ csshint: function (error, level) { var code; if (baidu.csshintRules[error.rule]) { code = baidu.csshintRules[error.rule](error); } else { code = maps.csshintMap[error.rule] || error.code; } var rule = maps.css[code]; var desc = error.message.replace(BAIDU_CODE_REG, ''); if (rule) { level.warn = rule.level < 2 ? 1 : 0; desc = rule.desc; } else { code = '999'; } return ' CSS' + code + ': ' + desc; }, /** * 针对 lesslint 校验错误的处理 * * @param {!Object} error lesslint 的错误对象 * @param {!Object} level 错误的等级数据 * @return {string} 对应的规则编码入描述信息 */ lesslint: function (error, level) { var code; if (baidu.lesslintRules[error.rule]) { code = baidu.lesslintRules[error.rule](error); } else { code = maps.lesslintMap[error.rule] || error.code; } var rule = maps.css[code]; var desc = error.message.replace(BAIDU_CODE_REG, ''); if (rule) { level.warn = rule.level < 2 ? 1 : 0; desc = rule.desc; } else { code = '999'; } return ' CSS' + code + ': ' + desc; }, /** * 针对 htmlcs 校验错误的处理 * * @param {!Object} error htmlcs 的错误对象 * @param {!Object} level 错误的等级数据 * @return {string} 对应的规则编码入描述信息 */ htmlcs: function (error, level) { var code; if (baidu.htmlcsRules[error.rule]) { code = baidu.htmlcsRules[error.rule](error); } else { code = maps.htmlcsMap[error.rule] || error.code; if (typeof code === 'object') { code = code[error.origin.code] || '999'; } } var rule = maps.html[code]; var desc = error.message.replace(BAIDU_CODE_REG, ''); if (rule) { level.warn = rule.level < 2 ? 1 : 0; desc = rule.desc; } else { code = '999'; } return 'HTML' + code + ': ' + desc; } }; /** * 校验错误信息输出报告 * * @param {vinyl.File} file 校验的 vinyl 文件对象 * @param {Array.<Object>} json 收集所有错误信息的数组 * @param {Function} filter 过滤函数组 * @param {Object} options cli 参数 * @return {boolean} 标记是否通过检查(仅当没有 ERROR 级别的) */ exports.transform = function (file, json, filter, options) { var log = this.log; var errors = file.errors; var item = {path: file.path, relative: file.relative}; // 对提示信息作排序 if (options.sort) { errors = errors.sort(function (a, b) { return a.line - b.line || a.column - b.column; }); } errors = item.errors = file.errors = filter(errors.reduce(function (errors, error) { var info = '→ '; // 全局性的错误可能没有位置信息 if (typeof error.line === 'number') { info += ('line ' + util.fixWidth(error.line, RULE_CODE_MAX_WIDTH)); if (typeof error.column === 'number') { info += (', col ' + util.fixWidth(error.column, RULE_CODE_MAX_WIDTH)); } info += ', '; } var coder = baidu[error.checker]; var level = {warn: 1}; var message = error.message.replace(BAIDU_CODE_REG, '').replace(/[\r\n]+/g, ''); var desc; if (coder) { desc = coder(error, level); // ignore none desc error if (!desc) { return errors; } // 某些规范描述过于抽象需要加上原文错误补充说明 if (~desc.indexOf('%s')) { desc = util.format(desc, message); } info += desc; } else { info += message; } var rule = error.rule; if (!rule) { rule = 'syntax'; level.warn = 0; } if (options.rule) { info += '\t(' + util.colorize(rule, options.color && 'gray') + ')'; } errors.push({ line: error.line, column: error.column, severity: level.warn ? 1 : 2, message: desc || message, rule: rule, info: info }); return errors; }, [])); var success = true; if (!errors.length) { return success; } console.log(); log.info('%s (%s message%s)', file.relative, errors.length, errors.length > 1 ? 's' : ''); errors.forEach(function (error) { // 仅当所有错误违反的是 [建议] 规则时算通过 success = success && error.severity === Severity.WARN; // [强制] 规则对应为 error,[建议] 对应为 warn log[error.severity === Severity.ERROR ? 'error' : 'warn'](error.info); }); console.log(); if (options.code) { item.code = file.contents.toString(); } json.push(item); return success; };