code-quality-lib 2.0.0 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +80 -263
- package/index.d.ts +25 -7
- package/index.js +347 -166
- package/package.json +11 -19
- package/.code-quality.json.example +0 -8
- package/.eslintrc.js +0 -121
- package/.prettierrc +0 -17
- package/knip.json +0 -76
- package/tsconfig.json +0 -41
package/index.js
CHANGED
|
@@ -4,199 +4,380 @@ const { execSync } = require('child_process');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
|
|
7
|
-
//
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
// ─── Package Manager Detection ──────────────────────────────────────────────
|
|
8
|
+
|
|
9
|
+
function detectPackageManager() {
|
|
10
|
+
const cwd = process.cwd();
|
|
11
|
+
const lockFiles = [
|
|
12
|
+
{ file: 'bun.lock', pm: 'bun' },
|
|
13
|
+
{ file: 'bun.lockb', pm: 'bun' },
|
|
14
|
+
{ file: 'pnpm-lock.yaml', pm: 'pnpm' },
|
|
15
|
+
{ file: 'yarn.lock', pm: 'yarn' },
|
|
16
|
+
{ file: 'package-lock.json', pm: 'npm' },
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
for (const { file, pm } of lockFiles) {
|
|
20
|
+
if (fs.existsSync(path.join(cwd, file))) return pm;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const binaries = ['bun', 'pnpm', 'yarn'];
|
|
24
|
+
for (const bin of binaries) {
|
|
25
|
+
try {
|
|
26
|
+
execSync(`which ${bin}`, { stdio: 'ignore' });
|
|
27
|
+
return bin;
|
|
28
|
+
} catch {
|
|
29
|
+
// not found, continue
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return 'npm';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function getExecPrefix(pm) {
|
|
37
|
+
const map = { bun: 'bunx', pnpm: 'pnpm dlx', yarn: 'yarn dlx', npm: 'npx' };
|
|
38
|
+
return map[pm] || 'npx';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function getRunPrefix(pm) {
|
|
42
|
+
const map = { bun: 'bun run', pnpm: 'pnpm run', yarn: 'yarn', npm: 'npm run' };
|
|
43
|
+
return map[pm] || 'npm run';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ─── Environment Loading ────────────────────────────────────────────────────
|
|
10
47
|
|
|
11
|
-
// Load environment variables from .env file
|
|
12
48
|
function loadEnvFile() {
|
|
13
49
|
try {
|
|
14
50
|
const envPath = path.join(process.cwd(), '.env');
|
|
15
|
-
if (fs.existsSync(envPath))
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
console.log('✅ Loaded environment variables from .env');
|
|
51
|
+
if (!fs.existsSync(envPath)) return;
|
|
52
|
+
|
|
53
|
+
const content = fs.readFileSync(envPath, 'utf8');
|
|
54
|
+
for (const line of content.split('\n')) {
|
|
55
|
+
const trimmed = line.trim();
|
|
56
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
57
|
+
const eqIndex = trimmed.indexOf('=');
|
|
58
|
+
if (eqIndex === -1) continue;
|
|
59
|
+
const key = trimmed.slice(0, eqIndex);
|
|
60
|
+
const value = trimmed.slice(eqIndex + 1);
|
|
61
|
+
if (key) process.env[key] = value;
|
|
30
62
|
}
|
|
31
|
-
} catch
|
|
32
|
-
|
|
63
|
+
} catch {
|
|
64
|
+
// silently continue without .env
|
|
33
65
|
}
|
|
34
66
|
}
|
|
35
67
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
console.log('\n🔍 Professional Code Quality Check\n');
|
|
39
|
-
console.log('─'.repeat(50));
|
|
40
|
-
console.log('📦 Using bun package manager\n');
|
|
68
|
+
// ─── Tool Path Resolution ───────────────────────────────────────────────────
|
|
41
69
|
|
|
42
|
-
|
|
43
|
-
|
|
70
|
+
function resolveToolBinDir() {
|
|
71
|
+
try {
|
|
72
|
+
return path.join(
|
|
73
|
+
path.dirname(require.resolve('code-quality-lib/package.json')),
|
|
74
|
+
'node_modules',
|
|
75
|
+
'.bin'
|
|
76
|
+
);
|
|
77
|
+
} catch {
|
|
78
|
+
return path.join(__dirname, 'node_modules', '.bin');
|
|
79
|
+
}
|
|
44
80
|
}
|
|
45
81
|
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
82
|
+
// ─── Config File Detection ──────────────────────────────────────────────────
|
|
83
|
+
|
|
84
|
+
function detectProjectConfigs() {
|
|
85
|
+
const cwd = process.cwd();
|
|
86
|
+
const configs = {
|
|
87
|
+
eslint: null,
|
|
88
|
+
prettier: null,
|
|
89
|
+
typescript: null,
|
|
90
|
+
knip: null,
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// ESLint config
|
|
94
|
+
const eslintFiles = [
|
|
95
|
+
'.eslintrc.js',
|
|
96
|
+
'.eslintrc.cjs',
|
|
97
|
+
'.eslintrc.json',
|
|
98
|
+
'.eslintrc.yml',
|
|
99
|
+
'.eslintrc.yaml',
|
|
100
|
+
'eslint.config.js',
|
|
101
|
+
'eslint.config.mjs',
|
|
102
|
+
];
|
|
103
|
+
for (const file of eslintFiles) {
|
|
104
|
+
const fullPath = path.join(cwd, file);
|
|
105
|
+
if (fs.existsSync(fullPath)) {
|
|
106
|
+
configs.eslint = fullPath;
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Prettier config
|
|
112
|
+
const prettierFiles = [
|
|
113
|
+
'.prettierrc',
|
|
114
|
+
'.prettierrc.json',
|
|
115
|
+
'.prettierrc.yml',
|
|
116
|
+
'.prettierrc.yaml',
|
|
117
|
+
'.prettierrc.js',
|
|
118
|
+
'.prettierrc.cjs',
|
|
119
|
+
'prettier.config.js',
|
|
120
|
+
'prettier.config.cjs',
|
|
121
|
+
];
|
|
122
|
+
for (const file of prettierFiles) {
|
|
123
|
+
const fullPath = path.join(cwd, file);
|
|
124
|
+
if (fs.existsSync(fullPath)) {
|
|
125
|
+
configs.prettier = fullPath;
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// TypeScript config
|
|
131
|
+
const tsconfigPath = path.join(cwd, 'tsconfig.json');
|
|
132
|
+
if (fs.existsSync(tsconfigPath)) {
|
|
133
|
+
configs.typescript = tsconfigPath;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Knip config
|
|
137
|
+
const knipFiles = ['knip.json', 'knip.jsonc', '.knip.json', '.knip.jsonc'];
|
|
138
|
+
for (const file of knipFiles) {
|
|
139
|
+
const fullPath = path.join(cwd, file);
|
|
140
|
+
if (fs.existsSync(fullPath)) {
|
|
141
|
+
configs.knip = fullPath;
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return configs;
|
|
61
147
|
}
|
|
62
148
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
{ name: 'TypeScript', cmd: `${tscPath} --noEmit`, description: 'Type checking and compilation' },
|
|
72
|
-
{ name: 'ESLint', cmd: `${eslintPath} . --ext .js,.jsx,.ts,.tsx`, description: 'Code linting and style checking' },
|
|
73
|
-
{ name: 'Prettier', cmd: `${prettierPath} --check .`, description: 'Code formatting validation' },
|
|
74
|
-
{ name: 'Knip', cmd: `${knipPath}`, description: 'Dead code detection' },
|
|
75
|
-
{ name: 'Snyk', cmd: `${snykPath} test --severity-threshold=high`, description: 'Security vulnerability scanning' }
|
|
149
|
+
// ─── Default Checks ─────────────────────────────────────────────────────────
|
|
150
|
+
|
|
151
|
+
const DEFAULT_TOOLS = [
|
|
152
|
+
{ name: 'TypeScript', bin: 'tsc', args: '--noEmit', description: 'Type checking and compilation' },
|
|
153
|
+
{ name: 'ESLint', bin: 'eslint', args: '. --ext .js,.jsx,.ts,.tsx', description: 'Code linting and style checking' },
|
|
154
|
+
{ name: 'Prettier', bin: 'prettier', args: '--check .', description: 'Code formatting validation' },
|
|
155
|
+
{ name: 'Knip', bin: 'knip', args: '', description: 'Dead code detection' },
|
|
156
|
+
{ name: 'Snyk', bin: 'snyk', args: 'test --severity-threshold=high', description: 'Security vulnerability scanning' },
|
|
76
157
|
];
|
|
77
158
|
|
|
78
|
-
|
|
79
|
-
const results = [];
|
|
159
|
+
// ─── CodeQualityChecker Class ───────────────────────────────────────────────
|
|
80
160
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
161
|
+
class CodeQualityChecker {
|
|
162
|
+
constructor(options = {}) {
|
|
163
|
+
this.options = {
|
|
164
|
+
loadEnv: options.loadEnv !== false,
|
|
165
|
+
useProjectConfig: options.useProjectConfig !== false,
|
|
166
|
+
tools: options.tools || DEFAULT_TOOLS.map((t) => t.name),
|
|
167
|
+
commands: options.commands || {},
|
|
168
|
+
descriptions: options.descriptions || {},
|
|
169
|
+
packageManager: options.packageManager || detectPackageManager(),
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
if (this.options.loadEnv) loadEnvFile();
|
|
173
|
+
|
|
174
|
+
// Detect project configs if useProjectConfig is enabled
|
|
175
|
+
this.projectConfigs = this.options.useProjectConfig ? detectProjectConfigs() : {};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
_getChecks() {
|
|
179
|
+
const binDir = resolveToolBinDir();
|
|
180
|
+
const checks = [];
|
|
181
|
+
|
|
182
|
+
for (const toolName of this.options.tools) {
|
|
183
|
+
const defaultTool = DEFAULT_TOOLS.find((t) => t.name === toolName);
|
|
184
|
+
if (!defaultTool && !this.options.commands[toolName]) continue;
|
|
185
|
+
|
|
186
|
+
let cmd = this.options.commands[toolName];
|
|
187
|
+
|
|
188
|
+
// If no custom command, build default with config detection
|
|
189
|
+
if (!cmd) {
|
|
190
|
+
const binPath = path.join(binDir, defaultTool.bin);
|
|
191
|
+
let args = defaultTool.args;
|
|
192
|
+
|
|
193
|
+
// Add config flags if project configs exist and useProjectConfig is true
|
|
194
|
+
if (this.options.useProjectConfig) {
|
|
195
|
+
if (toolName === 'ESLint' && this.projectConfigs.eslint) {
|
|
196
|
+
args = `${args} --config ${this.projectConfigs.eslint}`;
|
|
197
|
+
} else if (toolName === 'Prettier' && this.projectConfigs.prettier) {
|
|
198
|
+
args = `${args} --config ${this.projectConfigs.prettier}`;
|
|
199
|
+
} else if (toolName === 'TypeScript' && this.projectConfigs.typescript) {
|
|
200
|
+
args = `--project ${this.projectConfigs.typescript} --noEmit`;
|
|
201
|
+
} else if (toolName === 'Knip' && this.projectConfigs.knip) {
|
|
202
|
+
args = `--config ${this.projectConfigs.knip}`;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
cmd = `${binPath}${args ? ' ' + args : ''}`;
|
|
100
207
|
}
|
|
208
|
+
|
|
209
|
+
const description =
|
|
210
|
+
this.options.descriptions[toolName] ||
|
|
211
|
+
(defaultTool ? defaultTool.description : toolName);
|
|
212
|
+
|
|
213
|
+
checks.push({ name: toolName, cmd, description });
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return checks;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
runCommand(command, description) {
|
|
220
|
+
try {
|
|
221
|
+
const output = execSync(command, { stdio: 'pipe', encoding: 'utf8' });
|
|
222
|
+
return { success: true, output: (output || '').trim() };
|
|
223
|
+
} catch (error) {
|
|
224
|
+
const output = error.stdout || error.stderr || error.message || 'Unknown error';
|
|
225
|
+
return { success: false, output: output.trim() };
|
|
101
226
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
async run(options = {}) {
|
|
230
|
+
const showLogs = options.showLogs || false;
|
|
231
|
+
const checks = this._getChecks();
|
|
232
|
+
const pm = this.options.packageManager;
|
|
233
|
+
const runCmd = getRunPrefix(pm);
|
|
234
|
+
const results = [];
|
|
235
|
+
let allPassed = true;
|
|
236
|
+
|
|
237
|
+
console.log('\n🔍 Professional Code Quality Check\n');
|
|
238
|
+
console.log('─'.repeat(50));
|
|
239
|
+
console.log(`📦 Using ${pm} package manager`);
|
|
240
|
+
console.log(`⚙️ Config: ${this.options.useProjectConfig ? 'Project configs' : 'Bundled configs'}\n`);
|
|
241
|
+
|
|
110
242
|
if (showLogs) {
|
|
111
|
-
console.log(
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
console.log(
|
|
243
|
+
console.log('📋 Detailed error logging enabled (--logs flag)\n');
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
for (const { name, cmd, description } of checks) {
|
|
247
|
+
console.log(`Running ${name}...`);
|
|
248
|
+
const result = this.runCommand(cmd, description);
|
|
249
|
+
|
|
250
|
+
if (result.success) {
|
|
251
|
+
console.log(`✅ ${name}: Passed`);
|
|
252
|
+
} else {
|
|
253
|
+
allPassed = false;
|
|
254
|
+
console.log(`❌ ${name}: Failed`);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (showLogs && result.output) {
|
|
258
|
+
const icon = result.success ? '📄' : '❌';
|
|
259
|
+
console.log(`\n${icon} ${name} ${result.success ? 'Output' : 'Error Details'}:`);
|
|
260
|
+
console.log('─'.repeat(50));
|
|
261
|
+
console.log(result.output);
|
|
262
|
+
console.log('─'.repeat(50));
|
|
263
|
+
console.log('');
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
results.push({ name, description, ...result });
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Generate report
|
|
270
|
+
this._writeReport(results, allPassed, pm, runCmd);
|
|
271
|
+
|
|
272
|
+
console.log('\n' + '─'.repeat(50));
|
|
273
|
+
|
|
274
|
+
if (allPassed) {
|
|
275
|
+
console.log('\n🎉 All quality checks passed! Code is ready for production.\n');
|
|
276
|
+
} else {
|
|
277
|
+
console.log('\n❌ Some quality checks failed. Please fix the issues above.\n');
|
|
278
|
+
if (!showLogs) {
|
|
279
|
+
console.log('💡 Run with --logs flag to see detailed errors in terminal');
|
|
280
|
+
}
|
|
281
|
+
console.log('📄 See .quality-report.md for detailed error information\n');
|
|
116
282
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
283
|
+
|
|
284
|
+
return {
|
|
285
|
+
success: allPassed,
|
|
286
|
+
message: allPassed ? 'All quality checks passed' : 'Some quality checks failed',
|
|
287
|
+
results,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
_writeReport(results, allPassed, pm, runCmd) {
|
|
292
|
+
const reportPath = path.join(process.cwd(), '.quality-report.md');
|
|
293
|
+
const timestamp = new Date().toISOString();
|
|
294
|
+
const passed = results.filter((r) => r.success);
|
|
295
|
+
const failed = results.filter((r) => !r.success);
|
|
296
|
+
|
|
297
|
+
let report = `# Code Quality Report\n\n`;
|
|
298
|
+
report += `**Generated**: ${timestamp}\n`;
|
|
299
|
+
report += `**Package Manager**: ${pm}\n\n`;
|
|
300
|
+
report += `---\n\n`;
|
|
301
|
+
|
|
302
|
+
for (const r of results) {
|
|
303
|
+
report += `## ${r.name}\n\n`;
|
|
304
|
+
report += `**Description**: ${r.description}\n\n`;
|
|
305
|
+
report += `**Status**: ${r.success ? '✅ **PASSED**' : '❌ **FAILED**'}\n\n`;
|
|
306
|
+
if (r.output) {
|
|
307
|
+
report += `**Output**:\n\`\`\`\n${r.output}\n\`\`\`\n\n`;
|
|
308
|
+
}
|
|
309
|
+
report += `---\n\n`;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
report += `## Summary\n\n`;
|
|
313
|
+
report += `**Total Checks**: ${results.length}\n`;
|
|
314
|
+
report += `**Passed**: ${passed.length}\n`;
|
|
315
|
+
report += `**Failed**: ${failed.length}\n\n`;
|
|
316
|
+
|
|
317
|
+
if (allPassed) {
|
|
318
|
+
report += `### ✅ All quality checks passed!\n\nYour code is ready for production.\n\n`;
|
|
319
|
+
} else {
|
|
320
|
+
report += `### ❌ Some quality checks failed\n\n`;
|
|
321
|
+
report += `**Quick Fix Commands**:\n`;
|
|
322
|
+
report += `- \`${runCmd} lint:fix\` — Auto-fix linting issues\n`;
|
|
323
|
+
report += `- \`${runCmd} format:fix\` — Auto-format code\n\n`;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
report += `---\n\n## For AI Agents\n\n`;
|
|
327
|
+
report += `**Failed Checks**: ${failed.map((r) => r.name).join(', ') || 'None'}\n\n`;
|
|
328
|
+
if (!allPassed) {
|
|
329
|
+
report += `**Action Required**:\n`;
|
|
330
|
+
for (const r of failed) {
|
|
331
|
+
report += `- Fix ${r.name} errors\n`;
|
|
332
|
+
}
|
|
333
|
+
report += `\n`;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
try {
|
|
337
|
+
fs.writeFileSync(reportPath, report, 'utf8');
|
|
338
|
+
console.log(`\n📄 Quality report saved to: .quality-report.md`);
|
|
339
|
+
} catch (err) {
|
|
340
|
+
console.error(`\n⚠️ Failed to write report: ${err.message}`);
|
|
141
341
|
}
|
|
142
|
-
reportContent += `\n`;
|
|
143
|
-
|
|
144
|
-
results.push({ name, status: 'failed', error: errorOutput.trim() });
|
|
145
342
|
}
|
|
146
|
-
|
|
147
|
-
reportContent += `---\n\n`;
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
// Add summary
|
|
151
|
-
reportContent += `## Summary\n\n`;
|
|
152
|
-
reportContent += `**Total Checks**: ${checks.length}\n`;
|
|
153
|
-
reportContent += `**Passed**: ${results.filter(r => r.status === 'passed').length}\n`;
|
|
154
|
-
reportContent += `**Failed**: ${results.filter(r => r.status === 'failed').length}\n\n`;
|
|
155
|
-
|
|
156
|
-
if (allPassed) {
|
|
157
|
-
reportContent += `### ✅ All quality checks passed!\n\n`;
|
|
158
|
-
reportContent += `Your code is ready for production.\n\n`;
|
|
159
|
-
} else {
|
|
160
|
-
reportContent += `### ❌ Some quality checks failed\n\n`;
|
|
161
|
-
reportContent += `Please review the errors above and fix the issues.\n\n`;
|
|
162
|
-
reportContent += `**Quick Fix Commands**:\n`;
|
|
163
|
-
reportContent += `- \`bun run fix\` - Auto-fix linting and formatting\n`;
|
|
164
|
-
reportContent += `- \`bun run type:check\` - Check TypeScript errors\n`;
|
|
165
|
-
reportContent += `- \`bun run lint:check\` - Check ESLint errors\n`;
|
|
166
|
-
reportContent += `- \`bun run format:check\` - Check Prettier formatting\n\n`;
|
|
167
343
|
}
|
|
168
344
|
|
|
169
|
-
//
|
|
170
|
-
reportContent += `---\n\n`;
|
|
171
|
-
reportContent += `## For AI Agents\n\n`;
|
|
172
|
-
reportContent += `This report contains detailed error information for automated code quality fixes.\n\n`;
|
|
173
|
-
reportContent += `**Failed Checks**: ${results.filter(r => r.status === 'failed').map(r => r.name).join(', ') || 'None'}\n\n`;
|
|
174
|
-
if (!allPassed) {
|
|
175
|
-
reportContent += `**Action Required**:\n`;
|
|
176
|
-
results.filter(r => r.status === 'failed').forEach(r => {
|
|
177
|
-
reportContent += `- Fix ${r.name} errors\n`;
|
|
178
|
-
});
|
|
179
|
-
reportContent += `\n`;
|
|
180
|
-
}
|
|
345
|
+
// ─── Convenience Function ───────────────────────────────────────────────────
|
|
181
346
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
console.log(`\n📄 Quality report saved to: .quality-report.md`);
|
|
186
|
-
} catch (error) {
|
|
187
|
-
console.error(`\n⚠️ Failed to write report: ${error.message}`);
|
|
347
|
+
async function runQualityCheck(options = {}) {
|
|
348
|
+
const checker = new CodeQualityChecker(options);
|
|
349
|
+
return checker.run({ showLogs: options.showLogs || false });
|
|
188
350
|
}
|
|
189
351
|
|
|
190
|
-
|
|
352
|
+
// ─── CLI Entry Point ────────────────────────────────────────────────────────
|
|
353
|
+
|
|
354
|
+
if (require.main === module) {
|
|
355
|
+
const args = process.argv.slice(2);
|
|
191
356
|
|
|
192
|
-
if (
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
console.log('
|
|
357
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
358
|
+
console.log('Usage: code-quality [options]');
|
|
359
|
+
console.log('');
|
|
360
|
+
console.log('Options:');
|
|
361
|
+
console.log(' --help, -h Show this help message');
|
|
362
|
+
console.log(' --version, -v Show version number');
|
|
363
|
+
console.log(' --logs Show detailed error logs');
|
|
364
|
+
console.log('');
|
|
365
|
+
console.log('Runs TypeScript, ESLint, Prettier, Knip, and Snyk checks.');
|
|
366
|
+
process.exit(0);
|
|
199
367
|
}
|
|
200
|
-
|
|
201
|
-
|
|
368
|
+
|
|
369
|
+
if (args.includes('--version') || args.includes('-v')) {
|
|
370
|
+
const pkg = require('./package.json');
|
|
371
|
+
console.log(pkg.version);
|
|
372
|
+
process.exit(0);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const checker = new CodeQualityChecker();
|
|
376
|
+
checker.run({ showLogs: args.includes('--logs') }).then((result) => {
|
|
377
|
+
process.exit(result.success ? 0 : 1);
|
|
378
|
+
});
|
|
202
379
|
}
|
|
380
|
+
|
|
381
|
+
// ─── Exports ────────────────────────────────────────────────────────────────
|
|
382
|
+
|
|
383
|
+
module.exports = { CodeQualityChecker, runQualityCheck };
|
package/package.json
CHANGED
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "code-quality-lib",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "A configurable code quality checker library for Node.js projects",
|
|
5
5
|
"main": "index.js",
|
|
6
|
+
"types": "index.d.ts",
|
|
6
7
|
"bin": {
|
|
7
8
|
"code-quality": "index.js"
|
|
8
9
|
},
|
|
9
10
|
"scripts": {
|
|
10
11
|
"test": "node test/basic.test.js",
|
|
11
|
-
"test:ci": "node test/basic.test.js",
|
|
12
12
|
"start": "node index.js",
|
|
13
|
-
"
|
|
14
|
-
"format": "echo 'Formatting not configured yet'",
|
|
15
|
-
"build": "echo 'No build step required'",
|
|
16
|
-
"prepublishOnly": "echo 'Skipping tests for publishing'"
|
|
13
|
+
"prepublishOnly": "npm test"
|
|
17
14
|
},
|
|
18
15
|
"keywords": [
|
|
19
16
|
"code-quality",
|
|
@@ -24,40 +21,35 @@
|
|
|
24
21
|
"knip",
|
|
25
22
|
"quality-check"
|
|
26
23
|
],
|
|
27
|
-
"author": "",
|
|
24
|
+
"author": "NoonCore",
|
|
28
25
|
"license": "MIT",
|
|
29
26
|
"type": "commonjs",
|
|
30
27
|
"engines": {
|
|
31
28
|
"node": ">=18.0.0"
|
|
32
29
|
},
|
|
33
30
|
"dependencies": {
|
|
34
|
-
"typescript": "^5.8.3",
|
|
35
|
-
"eslint": "^9.18.0",
|
|
36
|
-
"prettier": "^3.4.2",
|
|
37
31
|
"@typescript-eslint/eslint-plugin": "^8.20.0",
|
|
38
32
|
"@typescript-eslint/parser": "^8.20.0",
|
|
33
|
+
"eslint": "^9.18.0",
|
|
39
34
|
"eslint-config-prettier": "^9.1.0",
|
|
35
|
+
"eslint-plugin-import": "^2.31.0",
|
|
40
36
|
"eslint-plugin-prettier": "^5.2.1",
|
|
41
37
|
"eslint-plugin-react": "^7.37.2",
|
|
42
38
|
"eslint-plugin-react-hooks": "^5.1.0",
|
|
43
39
|
"eslint-plugin-react-refresh": "^0.4.16",
|
|
40
|
+
"eslint-plugin-sonarjs": "^4.0.2",
|
|
44
41
|
"eslint-plugin-storybook": "^0.11.1",
|
|
45
|
-
"eslint-plugin-sonarjs": "^2.0.4",
|
|
46
42
|
"eslint-plugin-unicorn": "^57.0.0",
|
|
47
|
-
"eslint-plugin-import": "^2.31.0",
|
|
48
43
|
"knip": "^5.43.2",
|
|
49
|
-
"
|
|
44
|
+
"prettier": "^3.4.2",
|
|
45
|
+
"snyk": "^1.1293.1",
|
|
46
|
+
"typescript": "^5.8.3"
|
|
50
47
|
},
|
|
51
48
|
"files": [
|
|
52
49
|
"index.js",
|
|
53
50
|
"index.d.ts",
|
|
54
51
|
"README.md",
|
|
55
|
-
"LICENSE"
|
|
56
|
-
".eslintrc.js",
|
|
57
|
-
".prettierrc",
|
|
58
|
-
"knip.json",
|
|
59
|
-
"tsconfig.json",
|
|
60
|
-
".code-quality.json.example"
|
|
52
|
+
"LICENSE"
|
|
61
53
|
],
|
|
62
54
|
"repository": {
|
|
63
55
|
"type": "git",
|