Compare commits

...

3 Commits

Author SHA1 Message Date
Joel
19de28aeea chore: remove chinese comment 2025-12-02 22:09:31 +08:00
Joel
5012685aa9 feat: detect all files 2025-12-02 16:50:07 +08:00
Joel
c269b044c3 feat: detect the cognitive-complexity of file 2025-12-02 15:43:33 +08:00
4 changed files with 220 additions and 17 deletions

1
web/.gitignore vendored
View File

@@ -54,3 +54,4 @@ package-lock.json
# mise
mise.toml
complexity-report.csv

128
web/calculate-complexity.js Normal file
View File

@@ -0,0 +1,128 @@
// https://www.sonarsource.com/blog/5-clean-code-tips-for-reducing-cognitive-complexity/
const fs = require('fs');
const path = require('path');
const { Linter } = require('eslint');
const sonarPlugin = require('eslint-plugin-sonarjs');
const tsParser = require('@typescript-eslint/parser');
const linter = new Linter();
const config = {
languageOptions: {
parser: tsParser,
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
ecmaFeatures: {
jsx: true
}
}
},
plugins: {
sonarjs: sonarPlugin,
},
rules: {
'sonarjs/cognitive-complexity': ['error', 0], // always show error
},
};
function getFileComplexity(filePath) {
try {
const code = fs.readFileSync(filePath, 'utf8');
const messages = linter.verify(code, config);
let totalFileComplexity = 0;
let maxFileComplexityIndex = 0;
const functionComplexities = [];
messages.forEach((msg) => {
// console.log(msg);
if (msg.ruleId === 'sonarjs/cognitive-complexity') {
const match = msg.message.match(/reduce its Cognitive Complexity from (\d+)/);
if (match && match[1]) {
const score = parseInt(match[1], 10);
totalFileComplexity += score;
if (score > functionComplexities[maxFileComplexityIndex]?.score || functionComplexities.length === 0) {
maxFileComplexityIndex = functionComplexities.length;
}
functionComplexities.push({
line: msg.line,
// functionName: extractFunctionName(code, msg.line),
score: score,
// message: msg.message
});
}
}
});
return {
file: filePath,
totalComplexity: totalFileComplexity,
maxComplexityInfo: functionComplexities[maxFileComplexityIndex],
details: functionComplexities
};
} catch (error) {
console.error(`Error processing file ${filePath}:`, error);
return null;
}
}
function collectTsxFiles(baseDir) {
const entries = fs.readdirSync(baseDir, { withFileTypes: true });
const files = [];
entries.forEach((entry) => {
const fullPath = path.join(baseDir, entry.name);
if (entry.isDirectory()) {
if (
entry.name === 'node_modules' ||
entry.name.startsWith('.') ||
entry.name === '__test__' ||
entry.name === '__tests__'
) {
return;
}
files.push(...collectTsxFiles(fullPath));
} else if (
entry.isFile() &&
entry.name.endsWith('.tsx') &&
!entry.name.endsWith('.spec.tsx') &&
!entry.name.endsWith('.test.tsx')
) {
files.push(fullPath);
}
});
return files;
}
function writeCsv(results, outputPath) {
const header = 'File,Total Complexity,Max Complexity,Max Complexity Line';
const rows = results.map(({ file, totalComplexity, maxComplexityInfo }) => {
const maxScore = maxComplexityInfo?.score ?? 0;
const maxLine = maxComplexityInfo?.line ?? '';
const targetFile = JSON.stringify(path.relative(process.cwd(), file));
return `${targetFile},${totalComplexity},${maxScore},${maxLine}`;
});
fs.writeFileSync(outputPath, [header, ...rows].join('\n'), 'utf8');
}
function main() {
const projectRoot = process.cwd();
const tsxFiles = collectTsxFiles(projectRoot);
const results = tsxFiles
.map(getFileComplexity)
.filter((item) => item && item.totalComplexity > 0)
.sort((a, b) => b.maxComplexityInfo?.score - a.maxComplexityInfo?.score);
const outputPath = path.join(projectRoot, 'complexity-report.csv');
writeCsv(results, outputPath);
console.log(`CSV report written to ${outputPath}`);
}
main();

View File

@@ -179,6 +179,7 @@
"@types/semver": "^7.7.1",
"@types/sortablejs": "^1.15.8",
"@types/uuid": "^10.0.0",
"@typescript-eslint/parser": "^8.48.0",
"autoprefixer": "^10.4.21",
"babel-loader": "^10.0.0",
"bing-translate-api": "^4.1.0",

107
web/pnpm-lock.yaml generated
View File

@@ -457,6 +457,9 @@ importers:
'@types/uuid':
specifier: ^10.0.0
version: 10.0.0
'@typescript-eslint/parser':
specifier: ^8.48.0
version: 8.48.0(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3)
autoprefixer:
specifier: ^10.4.21
version: 10.4.21(postcss@8.5.6)
@@ -3386,8 +3389,8 @@ packages:
eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/parser@8.46.2':
resolution: {integrity: sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==}
'@typescript-eslint/parser@8.48.0':
resolution: {integrity: sha512-jCzKdm/QK0Kg4V4IK/oMlRZlY+QOcdjv89U2NgKHZk1CYTj82/RVSx1mV/0gqCVMJ/DA+Zf/S4NBWNF8GQ+eqQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
@@ -3399,16 +3402,32 @@ packages:
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/project-service@8.48.0':
resolution: {integrity: sha512-Ne4CTZyRh1BecBf84siv42wv5vQvVmgtk8AuiEffKTUo3DrBaGYZueJSxxBZ8fjk/N3DrgChH4TOdIOwOwiqqw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/scope-manager@8.46.2':
resolution: {integrity: sha512-LF4b/NmGvdWEHD2H4MsHD8ny6JpiVNDzrSZr3CsckEgCbAGZbYM4Cqxvi9L+WqDMT+51Ozy7lt2M+d0JLEuBqA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/scope-manager@8.48.0':
resolution: {integrity: sha512-uGSSsbrtJrLduti0Q1Q9+BF1/iFKaxGoQwjWOIVNJv0o6omrdyR8ct37m4xIl5Zzpkp69Kkmvom7QFTtue89YQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/tsconfig-utils@8.46.2':
resolution: {integrity: sha512-a7QH6fw4S57+F5y2FIxxSDyi5M4UfGF+Jl1bCGd7+L4KsaUY80GsiF/t0UoRFDHAguKlBaACWJRmdrc6Xfkkag==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/tsconfig-utils@8.48.0':
resolution: {integrity: sha512-WNebjBdFdyu10sR1M4OXTt2OkMd5KWIL+LLfeH9KhgP+jzfDV/LI3eXzwJ1s9+Yc0Kzo2fQCdY/OpdusCMmh6w==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/type-utils@8.46.2':
resolution: {integrity: sha512-HbPM4LbaAAt/DjxXaG9yiS9brOOz6fabal4uvUmaUYe6l3K1phQDMQKBRUrr06BQkxkvIZVVHttqiybM9nJsLA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -3420,12 +3439,22 @@ packages:
resolution: {integrity: sha512-lNCWCbq7rpg7qDsQrd3D6NyWYu+gkTENkG5IKYhUIcxSb59SQC/hEQ+MrG4sTgBVghTonNWq42bA/d4yYumldQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/types@8.48.0':
resolution: {integrity: sha512-cQMcGQQH7kwKoVswD1xdOytxQR60MWKM1di26xSUtxehaDs/32Zpqsu5WJlXTtTTqyAVK8R7hvsUnIXRS+bjvA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/typescript-estree@8.46.2':
resolution: {integrity: sha512-f7rW7LJ2b7Uh2EiQ+7sza6RDZnajbNbemn54Ob6fRwQbgcIn+GWfyuHDHRYgRoZu1P4AayVScrRW+YfbTvPQoQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/typescript-estree@8.48.0':
resolution: {integrity: sha512-ljHab1CSO4rGrQIAyizUS6UGHHCiAYhbfcIZ1zVJr5nMryxlXMVWS3duFPSKvSUbFPwkXMFk1k0EMIjub4sRRQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/utils@8.46.2':
resolution: {integrity: sha512-sExxzucx0Tud5tE0XqR0lT0psBQvEpnpiul9XbGUB1QwpWJJAps1O/Z7hJxLGiZLBKMCutjTzDgmd1muEhBnVg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -3437,6 +3466,10 @@ packages:
resolution: {integrity: sha512-tUFMXI4gxzzMXt4xpGJEsBsTox0XbNQ1y94EwlD/CuZwFcQP79xfQqMhau9HsRc/J0cAPA/HZt1dZPtGn9V/7w==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/visitor-keys@8.48.0':
resolution: {integrity: sha512-T0XJMaRPOH3+LBbAfzR2jalckP1MSG/L9eUtY0DEzUyVaXJ/t6zN0nR7co5kz0Jko/nkSYCBRkz1djvjajVTTg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@ungap/structured-clone@1.3.0':
resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
@@ -8503,8 +8536,8 @@ snapshots:
'@eslint-community/eslint-plugin-eslint-comments': 4.5.0(eslint@9.38.0(jiti@1.21.7))
'@eslint/markdown': 7.4.1
'@stylistic/eslint-plugin': 5.5.0(eslint@9.38.0(jiti@1.21.7))
'@typescript-eslint/eslint-plugin': 8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3))(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3)
'@typescript-eslint/parser': 8.46.2(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3)
'@typescript-eslint/eslint-plugin': 8.46.2(@typescript-eslint/parser@8.48.0(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3))(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3)
'@typescript-eslint/parser': 8.48.0(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3)
'@vitest/eslint-plugin': 1.3.23(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3)
ansis: 4.2.0
cac: 6.7.14
@@ -8524,8 +8557,8 @@ snapshots:
eslint-plugin-regexp: 2.10.0(eslint@9.38.0(jiti@1.21.7))
eslint-plugin-toml: 0.12.0(eslint@9.38.0(jiti@1.21.7))
eslint-plugin-unicorn: 61.0.2(eslint@9.38.0(jiti@1.21.7))
eslint-plugin-unused-imports: 4.3.0(@typescript-eslint/eslint-plugin@8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3))(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3))(eslint@9.38.0(jiti@1.21.7))
eslint-plugin-vue: 10.5.1(@stylistic/eslint-plugin@5.5.0(eslint@9.38.0(jiti@1.21.7)))(@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3))(eslint@9.38.0(jiti@1.21.7))(vue-eslint-parser@10.2.0(eslint@9.38.0(jiti@1.21.7)))
eslint-plugin-unused-imports: 4.3.0(@typescript-eslint/eslint-plugin@8.46.2(@typescript-eslint/parser@8.48.0(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3))(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3))(eslint@9.38.0(jiti@1.21.7))
eslint-plugin-vue: 10.5.1(@stylistic/eslint-plugin@5.5.0(eslint@9.38.0(jiti@1.21.7)))(@typescript-eslint/parser@8.48.0(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3))(eslint@9.38.0(jiti@1.21.7))(vue-eslint-parser@10.2.0(eslint@9.38.0(jiti@1.21.7)))
eslint-plugin-yml: 1.19.0(eslint@9.38.0(jiti@1.21.7))
eslint-processor-vue-blocks: 2.0.0(@vue/compiler-sfc@3.5.22)(eslint@9.38.0(jiti@1.21.7))
globals: 16.4.0
@@ -11831,10 +11864,10 @@ snapshots:
dependencies:
'@types/yargs-parser': 21.0.3
'@typescript-eslint/eslint-plugin@8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3))(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3)':
'@typescript-eslint/eslint-plugin@8.46.2(@typescript-eslint/parser@8.48.0(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3))(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3)':
dependencies:
'@eslint-community/regexpp': 4.12.1
'@typescript-eslint/parser': 8.46.2(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3)
'@typescript-eslint/parser': 8.48.0(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3)
'@typescript-eslint/scope-manager': 8.46.2
'@typescript-eslint/type-utils': 8.46.2(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3)
'@typescript-eslint/utils': 8.46.2(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3)
@@ -11848,12 +11881,12 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3)':
'@typescript-eslint/parser@8.48.0(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3)':
dependencies:
'@typescript-eslint/scope-manager': 8.46.2
'@typescript-eslint/types': 8.46.2
'@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.46.2
'@typescript-eslint/scope-manager': 8.48.0
'@typescript-eslint/types': 8.48.0
'@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.48.0
debug: 4.4.3
eslint: 9.38.0(jiti@1.21.7)
typescript: 5.9.3
@@ -11869,15 +11902,33 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/project-service@8.48.0(typescript@5.9.3)':
dependencies:
'@typescript-eslint/tsconfig-utils': 8.48.0(typescript@5.9.3)
'@typescript-eslint/types': 8.48.0
debug: 4.4.3
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
'@typescript-eslint/scope-manager@8.46.2':
dependencies:
'@typescript-eslint/types': 8.46.2
'@typescript-eslint/visitor-keys': 8.46.2
'@typescript-eslint/scope-manager@8.48.0':
dependencies:
'@typescript-eslint/types': 8.48.0
'@typescript-eslint/visitor-keys': 8.48.0
'@typescript-eslint/tsconfig-utils@8.46.2(typescript@5.9.3)':
dependencies:
typescript: 5.9.3
'@typescript-eslint/tsconfig-utils@8.48.0(typescript@5.9.3)':
dependencies:
typescript: 5.9.3
'@typescript-eslint/type-utils@8.46.2(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3)':
dependencies:
'@typescript-eslint/types': 8.46.2
@@ -11892,6 +11943,8 @@ snapshots:
'@typescript-eslint/types@8.46.2': {}
'@typescript-eslint/types@8.48.0': {}
'@typescript-eslint/typescript-estree@8.46.2(typescript@5.9.3)':
dependencies:
'@typescript-eslint/project-service': 8.46.2(typescript@5.9.3)
@@ -11908,6 +11961,21 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/typescript-estree@8.48.0(typescript@5.9.3)':
dependencies:
'@typescript-eslint/project-service': 8.48.0(typescript@5.9.3)
'@typescript-eslint/tsconfig-utils': 8.48.0(typescript@5.9.3)
'@typescript-eslint/types': 8.48.0
'@typescript-eslint/visitor-keys': 8.48.0
debug: 4.4.3
minimatch: 9.0.5
semver: 7.7.3
tinyglobby: 0.2.15
ts-api-utils: 2.1.0(typescript@5.9.3)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
'@typescript-eslint/utils@8.46.2(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3)':
dependencies:
'@eslint-community/eslint-utils': 4.9.0(eslint@9.38.0(jiti@1.21.7))
@@ -11924,6 +11992,11 @@ snapshots:
'@typescript-eslint/types': 8.46.2
eslint-visitor-keys: 4.2.1
'@typescript-eslint/visitor-keys@8.48.0':
dependencies:
'@typescript-eslint/types': 8.48.0
eslint-visitor-keys: 4.2.1
'@ungap/structured-clone@1.3.0': {}
'@vitest/eslint-plugin@1.3.23(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3)':
@@ -13651,13 +13724,13 @@ snapshots:
semver: 7.7.3
strip-indent: 4.1.1
eslint-plugin-unused-imports@4.3.0(@typescript-eslint/eslint-plugin@8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3))(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3))(eslint@9.38.0(jiti@1.21.7)):
eslint-plugin-unused-imports@4.3.0(@typescript-eslint/eslint-plugin@8.46.2(@typescript-eslint/parser@8.48.0(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3))(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3))(eslint@9.38.0(jiti@1.21.7)):
dependencies:
eslint: 9.38.0(jiti@1.21.7)
optionalDependencies:
'@typescript-eslint/eslint-plugin': 8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3))(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3)
'@typescript-eslint/eslint-plugin': 8.46.2(@typescript-eslint/parser@8.48.0(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3))(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3)
eslint-plugin-vue@10.5.1(@stylistic/eslint-plugin@5.5.0(eslint@9.38.0(jiti@1.21.7)))(@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3))(eslint@9.38.0(jiti@1.21.7))(vue-eslint-parser@10.2.0(eslint@9.38.0(jiti@1.21.7))):
eslint-plugin-vue@10.5.1(@stylistic/eslint-plugin@5.5.0(eslint@9.38.0(jiti@1.21.7)))(@typescript-eslint/parser@8.48.0(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3))(eslint@9.38.0(jiti@1.21.7))(vue-eslint-parser@10.2.0(eslint@9.38.0(jiti@1.21.7))):
dependencies:
'@eslint-community/eslint-utils': 4.9.0(eslint@9.38.0(jiti@1.21.7))
eslint: 9.38.0(jiti@1.21.7)
@@ -13669,7 +13742,7 @@ snapshots:
xml-name-validator: 4.0.0
optionalDependencies:
'@stylistic/eslint-plugin': 5.5.0(eslint@9.38.0(jiti@1.21.7))
'@typescript-eslint/parser': 8.46.2(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3)
'@typescript-eslint/parser': 8.48.0(eslint@9.38.0(jiti@1.21.7))(typescript@5.9.3)
eslint-plugin-yml@1.19.0(eslint@9.38.0(jiti@1.21.7)):
dependencies: