UNPKG

@ygyg/yg-cli

Version:

A simple CLI for front-end engineering automation construction tool.

398 lines (363 loc) 12.2 kB
const { exec } = require('child_process'); const { ESLint } = require('eslint'); // const { readFileSync } = require('fs'); const { relative } = require('path'); const stripAnsi = require('strip-ansi'); const stylelint = require('stylelint'); const utils = require('./utils'); const fs = require('fs'); const path = require('path'); const fglob = require('fast-glob'); const ignore = require('ignore'); // const esResult = require('../docs/EslintResults'); const robotMessage = require('./robotMessage'); const _ = require('lodash'); const rootPath = process.cwd(); const { get, sortBy, sum, isString } = _; // 初始化一个进度条长度为 50 的 ProgressBar 实例 ,对eslint 异步和同步效果不明显 const lintConfig = { esLintType: 0, // lint类型 与chaos 的 table对齐 esLintPath: '', styleLintType: 1, styleLintPath: '', }; const stats = { js: { linting: 0, warningCount: 0, errorCount: 0, fileCount: 0 }, style: { linting: 0, warningCount: 0, errorCount: 0, fileCount: 0 }, }; exports.checkGroup = async function() { // const myGroup = await utils.getLintGroup(); // Object.keys(myGroup).map(async function (group_key) { // // utils.logStep(`group_key=${group_key}`); // const lintPath = await utils.getLintPath({module_key: group_key}); // await lintCheck(lintPath, `group_key=${group_key}`); // 'app/web/src/' // // const results = esResult.EslintResults(); // // utils.logStep(JSON.stringify(results)); // // formatEslintResults(esResult); // return await myGroup[group_key]; // }); }; exports.checkList = async function(type) { if (type === 'all') { const lintPath = await utils.getLintPath(); lintCheck(lintPath); } else if (type === 'group') { this.checkGroup(); } else { const GITDIFF = 'git diff --cached --diff-filter=ACMR --name-only'; // 执行 git 的命令 exec(GITDIFF, (error, stdout) => { if (error) { console.error(`exec error: ${error}`); } // 对返回结果进行处理,拿到要检查的文件列表 const diffFileArray = stdout .split('\n') .filter((diffFile) => /(\.js|\.jsx|\.ts|\.tsx)(\n|$)/gi.test(diffFile)); utils.logStep('待检查的文件:', diffFileArray); lintCheck(diffFileArray); }); } }; async function lintCheck(diffFileArray, group_key = '') { utils.logStep(diffFileArray); if (diffFileArray.length > 0) { // await downloading(); const [esResult, styleResult] = await Promise.all([ esLint_Check(diffFileArray), styleLint_Check(diffFileArray), ]); utils.logStep('Checking complete...'); outputLint([ { fileCount: esResult.fileCount, errorCount: esResult.errorCount, warningCount: esResult.warningCount, lintType: lintConfig.esLintType, }, { fileCount: styleResult.fileCount, errorCount: styleResult.errorCount, warningCount: styleResult.warningCount, lintType: lintConfig.styleLintType, }, ]); } } async function esLint_Check(diffFileArray) { utils.logStep('Now Checking Eslint...'); // utils.logStep(`lintPath=${JSON.stringify(diffFileArray)}`); const extensions = ['.js', '.jsx', '.ts', '.tsx']; const esFiles = await getLintFiles(diffFileArray, '.eslintignore', extensions); const linter = new ESLint({ extensions: extensions, errorOnUnmatchedPattern: true, }); // --no-error-on-unmatched-pattern // console.log(process.cwd(), diffFileArray); // 2. Lint files. const eslintResults = await linter.lintFiles(diffFileArray); // 3. Format the results. const formatter = await linter.loadFormatter('stylish'); if (!Array.isArray(eslintResults)) { utils.logStep('无错误和警告'); return; } utils.logStep(formatter.format(eslintResults)); const warningCount = eslintResults.reduce( (prev, cur) => prev + cur.warningCount, 0, ); const errorCount = eslintResults.reduce( (prev, cur) => prev + cur.errorCount, 0, ); stats.js = { linting: warningCount + errorCount, warningCount, errorCount, fileCount: esFiles.length, }; // const esResult = await esLint_Check(lintPath); // console.log(eslintResults); // // // utils.logStep(11, results); // utils.logStep('all文件分组'); // formatEslintResults(eslintResults); // 格式化 排行榜 return { warningCount, errorCount, fileCount: esFiles.length }; } async function getLintFiles(srcPath, ignorefile, extensions) { const ig = ignore(); const distignoreFile = path.join(process.cwd(), ignorefile); if (fs.existsSync(distignoreFile)) { ig.add(fs.readFileSync(distignoreFile).toString()); } const files = await fglob(['**', '!**/node_modules/**'], { dot: false, cwd: srcPath, absolute: false, }); const filterFiles = ig.filter(files).filter((item) => { const ext = path.extname(item); if (extensions.includes(ext)) {return item;} }); return filterFiles; } async function styleLint_Check(diffFileArray) { utils.logStep('Now Checking StyleLint...'); const extensions = ['.css', '.less', '.sass']; let styleFiles = await getLintFiles(diffFileArray, '.stylelintignore', extensions); const res = await stylelint.lint({ files: styleFiles, // app/web/src reportNeedlessDisables: true, }); // eslint-disable-next-line no-unused-vars for (const result of res.results) { if (result.warnings.length > 0) { // utils.logStep(result.warnings); const warnings = result.warnings; const warningCount = warnings.reduce((prev, cur) => { const currNum = cur.severity === 'warning' ? 1 : 0; return prev + currNum; }, 0); const errorCount = warnings.reduce((prev, cur) => { const currNum = cur.severity === 'error' ? 1 : 0; return prev + currNum; }, 0); stats.style.warningCount += warningCount; stats.style.errorCount += errorCount; } // else{ // utils.logStep('nonnonoon') // } } stats.style.linting = stats.style.warningCount + stats.style.errorCount; stats.style.fileCount = styleFiles.length; const stylelintOutput = require('stylelint/lib/formatters/stringFormatter')( res.results, ); utils.logStep(stylelintOutput.substr(0, stylelintOutput.length - 1)); utils.logStep(JSON.stringify(stats)); return { warningCount: stats.style.warningCount, errorCount: stats.style.errorCount, fileCount: stats.style.fileCount, }; } async function outputLint(opt) { // utils.logStep(process.argv); const [ node, js, gitUserId, projectId = -1, pipelineId = -1, isDev = process.env.ISDEV || false, ] = process.argv; utils.logStep(node, js); const commits = await utils.getCommits(); // console.log(commits); // if (gitUserId === undefined) { // return; // } let insightParams = null; if (Array.isArray(opt)) { insightParams = opt.map((option) => { const { errorCount, warningCount, fileCount, lintType } = option; return { gitUserId: gitUserId === undefined || typeof (gitUserId) === 'string' ? commits.email : parseInt(gitUserId, 10), projectId: projectId === -1 ? commits.projectName : parseInt(projectId, 10), pipelineId: parseInt(pipelineId, 10), shortSha: commits.commit, branch: commits.branch, fileCount, warningCount, errorCount, lintType, }; }); } else { utils.logStep('not array'); const { errorCount, warningCount, fileCount, lintType } = opt; insightParams = { gitUserId: parseInt(gitUserId, 10), projectId: parseInt(projectId, 10), pipelineId: parseInt(pipelineId, 10), fileCount, warningCount, errorCount, lintType, }; } utils.logStep(JSON.stringify(insightParams)); robotMessage.sendLintInsight(insightParams); } // checkList('all'); /** * filePath => 相对路径 * source => 删掉 * output => 删掉 */ const formatEslintResults = (results = []) => { results.forEach((v) => { const { filePath } = v; v.filePath = relative(rootPath, filePath); v.messages = v.messages.map((v2) => ({ ...v2, message: stripAnsi(v2.message), })); delete v.source; delete v.output; }); // const rank = getRankMessages(results); // utils.logStep('all文件分组'); // utils.logStep(JSON.stringify(rank)); // utils.logStep('从模块文件分组'); // const sorted = groupByFolder(results, function (item) { // const currFolder = item.filePath.substr(0, item.filePath.lastIndexOf('/')); // return [currFolder]; // }); // console.log(sorted); // const modules_group = [ // { name: '交易', path: 'app/web/src/Order' }, // { name: '金融', path: 'src/components/replenishment' }, // ]; // for (let i = 0; i < sorted.length; i += 1) { // const mySorted = sorted[i]; // // const sorted = results.filter((item) => // // item.filePath.includes(modules_group[i].path), // // ); // const currFolder = mySorted[0].filePath.substr(0, mySorted[0].filePath.lastIndexOf('/')); // utils.logStep(`--------start [${i}]------按模块统计分组排行结果`); // utils.logStep(`--------${currFolder}`); // utils.logStep(JSON.stringify(mySorted)); // // const rank = getRankMessages(mySorted, mySorted[i].path); // // const groupCount = groupBy(mySorted); // // utils.logStep(mySorted[i].path); // // utils.logStep(JSON.stringify(groupCount)); // // utils.logStep(JSON.stringify(rank)); // utils.logStep(`--------end [${i}]------分组排行结束`); // } }; function groupByFolder(array, f) { // debugger; const groups = {}; array.forEach(function(o) { const group = JSON.stringify(f(o)); groups[group] = groups[group] || []; groups[group].push(o); }); return Object.keys(groups).map(function(group) { return groups[group]; }); } const getMeta = (ruleId = '', key = '') => { const RulesMeta = []; const url = get(RulesMeta[ruleId], key, ''); // sonar if (!url && isString(ruleId) && ruleId.startsWith('sonar')) { const sonarRuleId = ruleId.split('/')[1]; return `https://github.com/SonarSource/eslint-plugin-sonarjs/blob/master/docs/rules/${sonarRuleId}.md`; } return url; }; function groupBy(eslintResults) { const warningCount = eslintResults.reduce( (prev, cur) => prev + cur.warningCount, 0, ); const errorCount = eslintResults.reduce( (prev, cur) => prev + cur.errorCount, 0, ); return { warningCount, errorCount }; } function localFlat(arr) { return arr.reduce((acc, val) => acc.concat(val), []); } const getRankMessages = (EslintResults) => { // utils.logStep(...EslintResults); // EslintResults 按模块过滤 // TODO: 此处 应该按模块过滤,然后拍平,吐出rank const Messages = localFlat([...EslintResults].map((v) => v.messages)); // utils.logStep(Messages, 33); const ruleIdList = [...new Set(Messages.map((v) => v.ruleId))]; const RankMessages = ruleIdList.map((v) => { const sameList = Messages.filter((v2) => v2.ruleId === v); const { severity } = sameList[0]; const { url, description } = getMeta(v, 'docs'); const fixable = getMeta(v, 'fixable'); const severityText = { 1: 'warning', 2: 'error', }[severity]; const count = sameList.length; return { ruleId: v, url, fixable, description, severityText, count, }; }); const SortRankMessages = sortBy(RankMessages, ['count']).reverse(); return SortRankMessages; }; const analysis = (EslintResults) => { const errorCount = sum(EslintResults.map((v) => v.errorCount)); const warningCount = sum(EslintResults.map((v) => v.warningCount)); const fixableCount = sum( EslintResults.map((v) => v.fixableErrorCount + v.fixableWarningCount), ); const fileCount = EslintResults.length; const successFileCount = EslintResults.filter( (v) => v.errorCount + v.warningCount === 0, ).length; return { errorCount, warningCount, fixableCount, fileCount, successFileCount, }; };