code-quality-lib 1.0.4 → 2.0.0

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.
Files changed (3) hide show
  1. package/README.md +43 -6
  2. package/index.js +148 -300
  3. package/package.json +19 -1
package/README.md CHANGED
@@ -18,6 +18,10 @@
18
18
  - ⚙️ Configurable tools and commands
19
19
  - 📚 TypeScript definitions included
20
20
  - 🔧 CLI and library usage
21
+ - 📄 Detailed error reports for developers and AI agents
22
+ - 🔍 --logs flag for verbose terminal output
23
+ - 🤖 AI-friendly structured error information
24
+ - 📦 **All dependencies bundled** - No need to install TypeScript, ESLint, Prettier, Knip, or Snyk separately!
21
25
 
22
26
  ## Installation
23
27
 
@@ -91,6 +95,9 @@ yarn add -D code-quality-lib && yarn code-quality
91
95
  # Run all quality checks
92
96
  code-quality
93
97
 
98
+ # Run with detailed error logs in terminal
99
+ code-quality --logs
100
+
94
101
  # Or use with npx (without installing)
95
102
  npx code-quality-lib
96
103
 
@@ -101,6 +108,28 @@ bunx code-quality-lib
101
108
  yarn code-quality
102
109
  ```
103
110
 
111
+ ### Error Reporting
112
+
113
+ The library automatically generates a detailed error report at `.quality-report.md` with:
114
+ - ✅ Status of each quality check
115
+ - 📋 Full error output for failed checks
116
+ - 💡 Suggestions for fixing common issues
117
+ - 🤖 AI-friendly structured information
118
+
119
+ **Viewing Errors:**
120
+ ```bash
121
+ # Silent mode (default) - errors saved to .quality-report.md
122
+ code-quality
123
+
124
+ # Verbose mode - errors shown in terminal + saved to report
125
+ code-quality --logs
126
+
127
+ # View the report
128
+ cat .quality-report.md
129
+ ```
130
+
131
+ **Note:** Add `.quality-report.md` to your `.gitignore` to keep reports local only.
132
+
104
133
  ### As a Library
105
134
 
106
135
  ```javascript
@@ -117,12 +146,20 @@ checker.run().then(result => {
117
146
 
118
147
  ### Default Tools
119
148
 
120
- The library runs these tools by default:
121
- - **TypeScript** - Type checking and compilation
122
- - **ESLint** - Code linting and style checking
123
- - **Prettier** - Code formatting validation
124
- - **Knip** - Dead code detection and unused exports
125
- - **Snyk** - Security vulnerability scanning
149
+ The library runs these tools by default (all bundled, no separate installation needed):
150
+ - **TypeScript** (v5.8.3) - Type checking and compilation
151
+ - **ESLint** (v9.18.0) - Code linting and style checking with plugins:
152
+ - @typescript-eslint/eslint-plugin & parser
153
+ - eslint-plugin-react & react-hooks
154
+ - eslint-plugin-prettier
155
+ - eslint-plugin-sonarjs
156
+ - eslint-plugin-unicorn
157
+ - eslint-plugin-import
158
+ - **Prettier** (v3.4.2) - Code formatting validation
159
+ - **Knip** (v5.43.2) - Dead code detection and unused exports
160
+ - **Snyk** (v1.1293.1) - Security vulnerability scanning
161
+
162
+ **No need to install these tools separately!** Everything is bundled with the library.
126
163
 
127
164
  ### Custom Configuration
128
165
 
package/index.js CHANGED
@@ -4,6 +4,10 @@ const { execSync } = require('child_process');
4
4
  const path = require('path');
5
5
  const fs = require('fs');
6
6
 
7
+ // Parse command line arguments
8
+ const args = process.argv.slice(2);
9
+ const showLogs = args.includes('--logs');
10
+
7
11
  // Load environment variables from .env file
8
12
  function loadEnvFile() {
9
13
  try {
@@ -29,326 +33,170 @@ function loadEnvFile() {
29
33
  }
30
34
  }
31
35
 
32
- // Check if this is first run and setup configuration
33
- function checkFirstRun() {
34
- const configPath = path.join(process.cwd(), '.code-quality.json');
35
-
36
- if (!fs.existsSync(configPath)) {
37
- colorLog('\n🔧 Code Quality Library - First Time Setup', 'bright');
38
- colorLog('─'.repeat(50), 'cyan');
39
- colorLog('\nChoose your quality rules configuration:', 'yellow');
40
- colorLog('1) Use library rules (recommended for new projects)', 'white');
41
- colorLog('2) Use project rules (keep existing configurations)', 'white');
42
- colorLog('3) Custom setup (choose specific tools)', 'white');
43
-
44
- // For now, default to library rules
45
- // In a real implementation, you would read user input here
46
- const choice = process.env.CODE_QUALITY_CHOICE || '1';
47
-
48
- const config = {
49
- useLibraryRules: choice === '1',
50
- useProjectRules: choice === '2',
51
- customSetup: choice === '3',
52
- tools: ['TypeScript', 'ESLint', 'Prettier', 'Knip', 'Snyk'],
53
- copyConfigs: choice === '1',
54
- version: '1.0.1'
55
- };
56
-
57
- fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
58
-
59
- if (choice === '1') {
60
- colorLog('\n✅ Using library rules - Config files will be copied', 'green');
61
- } else if (choice === '2') {
62
- colorLog('\n✅ Using project rules - Existing configs preserved', 'green');
63
- } else {
64
- colorLog('\n✅ Custom setup - Configure as needed', 'green');
65
- }
66
-
67
- colorLog('\n💡 To change this later, edit: .code-quality.json', 'cyan');
68
- colorLog('💡 Or run: code-quality --config to reconfigure', 'cyan');
69
- colorLog('─'.repeat(50), 'cyan');
70
-
71
- return config;
72
- }
73
-
74
- try {
75
- return JSON.parse(fs.readFileSync(configPath, 'utf8'));
76
- } catch (error) {
77
- colorLog('⚠️ Invalid config file, using defaults', 'yellow');
78
- return {
79
- useLibraryRules: true,
80
- useProjectRules: false,
81
- customSetup: false,
82
- tools: ['TypeScript', 'ESLint', 'Prettier', 'Knip', 'Snyk'],
83
- copyConfigs: true
84
- };
85
- }
86
- }
36
+ loadEnvFile();
87
37
 
88
- // Copy config files from library to project
89
- function copyConfigFiles(packageManager) {
90
- const libPath = path.dirname(__dirname);
91
- const projectPath = process.cwd();
92
-
93
- const configs = [
94
- { src: '.eslintrc.js', dest: '.eslintrc.js' },
95
- { src: '.prettierrc', dest: '.prettierrc' },
96
- { src: 'knip.json', dest: 'knip.json' },
97
- { src: 'tsconfig.json', dest: 'tsconfig.json' }
98
- ];
99
-
100
- configs.forEach(({ src, dest }) => {
101
- const srcPath = path.join(libPath, src);
102
- const destPath = path.join(projectPath, dest);
103
-
104
- try {
105
- if (fs.existsSync(srcPath)) {
106
- fs.copyFileSync(srcPath, destPath);
107
- colorLog(`✅ Copied ${dest}`, 'green');
108
- }
109
- } catch (error) {
110
- colorLog(`⚠️ Could not copy ${dest}: ${error.message}`, 'yellow');
111
- }
112
- });
113
- }
38
+ console.log('\n🔍 Professional Code Quality Check\n');
39
+ console.log('─'.repeat(50));
40
+ console.log('📦 Using bun package manager\n');
114
41
 
115
- // Color codes for beautiful output
116
- const colors = {
117
- reset: '\x1b[0m',
118
- bright: '\x1b[1m',
119
- red: '\x1b[31m',
120
- green: '\x1b[32m',
121
- yellow: '\x1b[33m',
122
- blue: '\x1b[34m',
123
- magenta: '\x1b[35m',
124
- cyan: '\x1b[36m',
125
- white: '\x1b[37m',
126
- dim: '\x1b[2m',
127
- header: '\x1b[1m\x1b[36m'
128
- };
42
+ if (showLogs) {
43
+ console.log('📋 Detailed error logging enabled (--logs flag)\n');
44
+ }
129
45
 
130
- function colorLog(message, color = 'reset') {
131
- console.log(`${colors[color]}${message}${colors.reset}`);
46
+ // Prepare report
47
+ const reportPath = path.join(process.cwd(), '.quality-report.md');
48
+ const timestamp = new Date().toISOString();
49
+ let reportContent = `# Code Quality Report\n\n`;
50
+ reportContent += `**Generated**: ${timestamp}\n`;
51
+ reportContent += `**Package Manager**: npm\n\n`;
52
+ reportContent += `---\n\n`;
53
+
54
+ // Get paths to bundled tools - try to resolve from library location
55
+ let libPath;
56
+ try {
57
+ libPath = path.dirname(require.resolve('code-quality-lib/package.json'));
58
+ } catch {
59
+ // Fallback to current directory if running from library itself
60
+ libPath = __dirname;
132
61
  }
133
62
 
134
- // Detect package manager
135
- function detectPackageManager() {
136
- const cwd = process.cwd();
63
+ const tscPath = path.join(libPath, 'node_modules', '.bin', 'tsc');
64
+ const eslintPath = path.join(libPath, 'node_modules', '.bin', 'eslint');
65
+ const prettierPath = path.join(libPath, 'node_modules', '.bin', 'prettier');
66
+ const knipPath = path.join(libPath, 'node_modules', '.bin', 'knip');
67
+ const snykPath = path.join(libPath, 'node_modules', '.bin', 'snyk');
68
+
69
+ // Run quality checks using bundled dependencies
70
+ const checks = [
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' }
76
+ ];
77
+
78
+ let allPassed = true;
79
+ const results = [];
80
+
81
+ checks.forEach(({ name, cmd, description }) => {
82
+ console.log(`Running ${name}...`);
137
83
 
138
- if (fs.existsSync(path.join(cwd, 'bun.lockb'))) {
139
- return 'bun';
140
- } else if (fs.existsSync(path.join(cwd, 'pnpm-lock.yaml'))) {
141
- return 'pnpm';
142
- } else if (fs.existsSync(path.join(cwd, 'yarn.lock'))) {
143
- return 'yarn';
144
- } else if (fs.existsSync(path.join(cwd, 'package-lock.json'))) {
145
- return 'npm';
146
- }
84
+ reportContent += `## ${name}\n\n`;
85
+ reportContent += `**Description**: ${description}\n\n`;
86
+ reportContent += `**Command**: \`${cmd}\`\n\n`;
147
87
 
148
- // Default to npm if no lock file found
149
- return 'npm';
150
- }
151
-
152
- // Get package manager run command
153
- function getRunCommand(packageManager) {
154
- switch (packageManager) {
155
- case 'bun': return 'bun run';
156
- case 'pnpm': return 'pnpm run';
157
- case 'yarn': return 'yarn';
158
- case 'npm': return 'npm run';
159
- default: return 'npm run';
160
- }
161
- }
162
-
163
- // Get package manager exec command
164
- function getExecCommand(packageManager) {
165
- switch (packageManager) {
166
- case 'bun': return 'bunx';
167
- case 'pnpm': return 'pnpm dlx';
168
- case 'yarn': return 'yarn dlx';
169
- case 'npm': return 'npx';
170
- default: return 'npx';
171
- }
172
- }
173
-
174
- // Main class for the library
175
- class CodeQualityChecker {
176
- constructor(options = {}) {
177
- // Check first run configuration
178
- const config = checkFirstRun();
179
-
180
- this.options = {
181
- loadEnv: true,
182
- tools: config.tools || ['TypeScript', 'ESLint', 'Prettier', 'Knip', 'Snyk'],
183
- packageManager: detectPackageManager(),
184
- copyConfigs: config.copyConfigs !== false,
185
- useLibraryRules: config.useLibraryRules !== false,
186
- useProjectRules: config.useProjectRules || false,
187
- customSetup: config.customSetup || false,
188
- ...options
189
- };
88
+ try {
89
+ const output = execSync(cmd, { stdio: 'pipe', encoding: 'utf8' });
90
+ console.log(`✅ ${name}: Passed`);
190
91
 
191
- if (this.options.loadEnv) {
192
- loadEnvFile();
92
+ reportContent += `**Status**: ✅ **PASSED**\n\n`;
93
+ if (output && output.trim()) {
94
+ reportContent += `**Output**:\n\`\`\`\n${output.trim()}\n\`\`\`\n\n`;
95
+
96
+ if (showLogs) {
97
+ console.log(`\n📄 ${name} Output:`);
98
+ console.log(output.trim());
99
+ console.log('');
100
+ }
193
101
  }
194
102
 
195
- if (this.options.copyConfigs && this.options.useLibraryRules) {
196
- copyConfigFiles(this.options.packageManager);
197
- }
198
- }
199
-
200
- runCommand(command, description) {
201
- const startTime = Date.now();
202
- const result = {
203
- success: true,
204
- message: ''
205
- };
103
+ results.push({ name, status: 'passed', output: output.trim() });
104
+ } catch (error) {
105
+ allPassed = false;
106
+ const errorOutput = error.stdout || error.stderr || error.message || 'Unknown error';
107
+ console.log(`❌ ${name}: Failed`);
206
108
 
207
- try {
208
- const output = execSync(command, { encoding: 'utf8' });
209
- result.message = output.trim();
210
- } catch (error) {
211
- result.success = false;
212
- result.message = error.stdout.trim();
109
+ // Show errors in terminal if --logs flag is present
110
+ if (showLogs) {
111
+ console.log(`\n❌ ${name} Error Details:`);
112
+ console.log('─'.repeat(50));
113
+ console.log(errorOutput.trim());
114
+ console.log('─'.repeat(50));
115
+ console.log('');
213
116
  }
214
117
 
215
- const endTime = Date.now();
216
- const duration = (endTime - startTime) / 1000;
217
-
218
- return {
219
- ...result,
220
- duration
221
- };
222
- }
223
-
224
- formatOutput(tool, result) {
225
- return formatOutput(tool, result);
226
- }
227
-
228
- checkSnykToken() {
229
- return checkSnykToken();
230
- }
231
-
232
- async run() {
233
- console.log(colors.header('\n🔍 Professional Code Quality Check\n'));
234
- console.log(colors.dim('─'.repeat(50)));
118
+ reportContent += `**Status**: ❌ **FAILED**\n\n`;
119
+ reportContent += `**Error Output**:\n\`\`\`\n${errorOutput.trim()}\n\`\`\`\n\n`;
235
120
 
236
- // Show detected package manager
237
- console.log(colors.dim(`📦 Using ${this.options.packageManager} package manager`));
238
-
239
- // Check Snyk authentication first
240
- const snykAuthenticated = this.checkSnykToken();
241
-
242
- const results = this.options.tools.map(tool => ({
243
- tool,
244
- command: tool === 'Snyk' && !snykAuthenticated
245
- ? `echo "Authentication required - add SNYK_TOKEN to .env or run ${getExecCommand(this.options.packageManager)} snyk auth"`
246
- : this.options.commands[tool],
247
- description: this.options.descriptions[tool]
248
- }));
249
-
250
- let allPassed = true;
251
- const outputs = [];
252
-
253
- for (const { tool, command, description } of results) {
254
- const result = this.runCommand(command, description);
255
- const output = this.formatOutput(tool, result);
256
- outputs.push(output);
257
- if (!result.success) {
258
- allPassed = false;
259
- }
260
- }
261
-
262
- const args = process.argv.slice(2);
263
- for (const arg of args) {
264
- if (arg === '--no-configs') {
265
- this.options.copyConfigs = false;
266
- } else if (arg === '--config') {
267
- this.options.reconfigure = true;
268
- }
269
- }
270
-
271
- console.log('\n');
272
- outputs.forEach((output) => console.log(output));
273
-
274
- console.log(colors.dim('─'.repeat(50)));
275
-
276
- if (allPassed) {
277
- console.log(
278
- '\n🎉 All quality checks passed! Code is ready for production.\n'
279
- );
280
- return { success: true, message: 'All checks passed' };
281
- } else {
282
- console.log(
283
- '\n❌ Some quality checks failed. Please fix the issues above.\n'
284
- );
285
- return { success: false, message: 'Some checks failed' };
121
+ // Add suggestions for common issues
122
+ reportContent += `**Suggestions**:\n`;
123
+ if (name === 'TypeScript') {
124
+ reportContent += `- Check type errors in the output above\n`;
125
+ reportContent += `- Run \`npx tsc --noEmit\` to see detailed errors\n`;
126
+ reportContent += `- Fix type mismatches and missing type definitions\n`;
127
+ } else if (name === 'ESLint') {
128
+ reportContent += `- Run \`bun run lint:fix\` to auto-fix issues\n`;
129
+ reportContent += `- Check ESLint errors in the output above\n`;
130
+ reportContent += `- Review and fix code style violations\n`;
131
+ } else if (name === 'Prettier') {
132
+ reportContent += `- Run \`bun run format:fix\` to auto-format files\n`;
133
+ reportContent += `- Check formatting issues in the output above\n`;
134
+ } else if (name === 'Knip') {
135
+ reportContent += `- Remove unused exports and dependencies\n`;
136
+ reportContent += `- Check dead code in the output above\n`;
137
+ } else if (name === 'Snyk') {
138
+ reportContent += `- Review security vulnerabilities in the output above\n`;
139
+ reportContent += `- Update vulnerable dependencies\n`;
140
+ reportContent += `- Run \`npx snyk wizard\` for guided fixes\n`;
286
141
  }
142
+ reportContent += `\n`;
143
+
144
+ results.push({ name, status: 'failed', error: errorOutput.trim() });
287
145
  }
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`;
288
167
  }
289
168
 
290
- // Export both the class and a simple function
291
- module.exports = { CodeQualityChecker };
292
-
293
- // If run directly, execute with default options
294
- if (require.main === module) {
295
- const args = process.argv.slice(2);
296
- const options = {};
297
-
298
- // Check for reconfigure flag
299
- if (args.includes('--config')) {
300
- // Remove existing config and run setup again
301
- const configPath = path.join(process.cwd(), '.code-quality.json');
302
- if (fs.existsSync(configPath)) {
303
- fs.unlinkSync(configPath);
304
- colorLog('🔧 Configuration reset - Running setup again', 'yellow');
305
- }
306
- }
307
-
308
- // Parse command line arguments
309
- for (let i = 0; i < args.length; i++) {
310
- const arg = args[i];
311
- if (arg === '--no-env') {
312
- options.loadEnv = false;
313
- } else if (arg === '--no-configs') {
314
- options.copyConfigs = false;
315
- } else if (arg.startsWith('--tools=')) {
316
- options.tools = arg.split('=')[1].split(',');
317
- } else if (arg === '--help' || arg === '-h') {
318
- console.log(`
319
- Professional Code Quality Checker
169
+ // Add AI Agent section
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
+ }
320
181
 
321
- Usage: code-quality [options]
182
+ // Write report to file (overwrite existing)
183
+ try {
184
+ fs.writeFileSync(reportPath, reportContent, 'utf8');
185
+ console.log(`\n📄 Quality report saved to: .quality-report.md`);
186
+ } catch (error) {
187
+ console.error(`\n⚠️ Failed to write report: ${error.message}`);
188
+ }
322
189
 
323
- Options:
324
- --no-env Skip loading .env file
325
- --no-configs Skip copying config files
326
- --config Reconfigure setup choices
327
- --tools=tools Comma-separated list of tools to run
328
- (TypeScript,ESLint,Prettier,Knip,Snyk)
329
- --help, -h Show this help message
190
+ console.log('\n' + '─'.repeat(50));
330
191
 
331
- Examples:
332
- code-quality # Run all tools
333
- code-quality --tools=ESLint # Run only ESLint
334
- code-quality --no-env # Skip .env loading
335
- code-quality --config # Reconfigure setup
336
- `);
337
- process.exit(0);
338
- }
192
+ if (allPassed) {
193
+ console.log('\n🎉 All quality checks passed! Code is ready for production.\n');
194
+ process.exit(0);
195
+ } else {
196
+ console.log('\n❌ Some quality checks failed. Please fix the issues above.\n');
197
+ if (!showLogs) {
198
+ console.log('💡 Run with --logs flag to see detailed errors in terminal');
339
199
  }
340
-
341
- if (args.includes('--version') || args.includes('-v')) {
342
- const pkg = require('./package.json');
343
- console.log(pkg.version);
344
- process.exit(0);
345
- }
346
-
347
- const checker = new CodeQualityChecker(options);
348
- checker.run().then(({ success }) => {
349
- process.exit(success ? 0 : 1);
350
- }).catch(error => {
351
- console.error('Error running quality checks:', error);
352
- process.exit(1);
353
- });
200
+ console.log('📄 See .quality-report.md for detailed error information\n');
201
+ process.exit(1);
354
202
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "code-quality-lib",
3
- "version": "1.0.4",
3
+ "version": "2.0.0",
4
4
  "description": "A configurable code quality checker library for Node.js projects",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -30,6 +30,24 @@
30
30
  "engines": {
31
31
  "node": ">=18.0.0"
32
32
  },
33
+ "dependencies": {
34
+ "typescript": "^5.8.3",
35
+ "eslint": "^9.18.0",
36
+ "prettier": "^3.4.2",
37
+ "@typescript-eslint/eslint-plugin": "^8.20.0",
38
+ "@typescript-eslint/parser": "^8.20.0",
39
+ "eslint-config-prettier": "^9.1.0",
40
+ "eslint-plugin-prettier": "^5.2.1",
41
+ "eslint-plugin-react": "^7.37.2",
42
+ "eslint-plugin-react-hooks": "^5.1.0",
43
+ "eslint-plugin-react-refresh": "^0.4.16",
44
+ "eslint-plugin-storybook": "^0.11.1",
45
+ "eslint-plugin-sonarjs": "^2.0.4",
46
+ "eslint-plugin-unicorn": "^57.0.0",
47
+ "eslint-plugin-import": "^2.31.0",
48
+ "knip": "^5.43.2",
49
+ "snyk": "^1.1293.1"
50
+ },
33
51
  "files": [
34
52
  "index.js",
35
53
  "index.d.ts",