code-quality-lib 1.1.0 → 2.0.1

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 CHANGED
@@ -1,315 +1,134 @@
1
1
  # Code Quality Library
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/code-quality-lib)](https://www.npmjs.com/package/code-quality-lib)
4
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
- [![Node.js Version](https://img.shields.io/badge/node-18.x%20%7C%2020.x%20%7C%2022.x%20%7C%2025.x-brightgreen)](https://nodejs.org/)
6
- [![Bun Version](https://img.shields.io/badge/bun-1.3.x-black)](https://bun.sh/)
7
- [![pnpm Version](https://img.shields.io/badge/pnpm-10.x-f69220)](https://pnpm.io/)
8
- [![Yarn Version](https://img.shields.io/badge/yarn-4.13.0-2c8ebb)](https://yarnpkg.com/)
9
4
  [![CI/CD](https://github.com/NoonCore/code-quality-lib/actions/workflows/ci.yml/badge.svg)](https://github.com/NoonCore/code-quality-lib/actions/workflows/ci.yml)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen)](https://nodejs.org/)
10
7
  [![TypeScript](https://img.shields.io/badge/TypeScript-included-blue)](https://www.typescriptlang.org/)
11
8
 
12
- > 🚀 A configurable code quality checker for Node.js that auto-detects your package manager and runs TypeScript, ESLint, Prettier, Knip, and Snyk.
9
+ > A configurable code quality checker for Node.js auto-detects your package manager and runs **TypeScript**, **ESLint**, **Prettier**, **Knip**, and **Snyk** with all dependencies bundled.
13
10
 
14
11
  ## Features
15
12
 
16
- - 🚀 Works with npm, bun, pnpm, yarn (auto-detected)
17
- - 🎨 Beautiful terminal output
18
- - ⚙️ Configurable tools and commands
19
- - 📚 TypeScript definitions included
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
13
+ - **All tools bundled** — no need to install TypeScript, ESLint, Prettier, Knip, or Snyk separately
14
+ - **Auto-detects package manager** — npm, bun, pnpm, yarn
15
+ - **CLI + Library** use from terminal or programmatically
16
+ - **Detailed reports** generates `.quality-report.md` with AI-friendly error info
17
+ - **`--logs` flag** verbose terminal output for debugging
18
+ - **TypeScript definitions** full type safety included
24
19
 
25
20
  ## Installation
26
21
 
27
- ### npm
28
- ```bash
29
- # Install as development dependency (recommended)
30
- npm install -D code-quality-lib
31
-
32
- # Install globally for CLI usage
33
- npm install -g code-quality-lib
34
-
35
- # Install from GitHub (development)
36
- npm install -D https://github.com/NoonCore/code-quality-lib.git
37
- ```
38
-
39
- ### bun (recommended - faster)
40
- ```bash
41
- # Install as development dependency (recommended)
42
- bun add -D code-quality-lib
43
-
44
- # Install globally for CLI usage
45
- bun add -g code-quality-lib
46
-
47
- # Install from GitHub (development)
48
- bun add -D https://github.com/NoonCore/code-quality-lib.git
49
- ```
50
-
51
- ### pnpm
52
22
  ```bash
53
- # Install as development dependency (recommended)
54
- pnpm add -D code-quality-lib
55
-
56
- # Install globally for CLI usage
57
- pnpm add -g code-quality-lib
58
-
59
- # Install from GitHub (development)
60
- pnpm add -D https://github.com/NoonCore/code-quality-lib.git
61
- ```
62
-
63
- ### yarn
64
- ```bash
65
- # Install as development dependency (recommended)
66
- yarn add -D code-quality-lib
67
-
68
- # Install globally for CLI usage
69
- yarn global add code-quality-lib
70
-
71
- # Install from GitHub (development)
72
- yarn add -D https://github.com/NoonCore/code-quality-lib.git
23
+ npm install -D code-quality-lib # npm
24
+ bun add -D code-quality-lib # bun
25
+ pnpm add -D code-quality-lib # pnpm
26
+ yarn add -D code-quality-lib # yarn
73
27
  ```
74
28
 
75
-
76
29
  ## Quick Start
77
30
 
78
31
  ```bash
79
- # Install and run
80
- npm install -D code-quality-lib && npx code-quality
81
-
82
- # Or with bun
83
- bun add -D code-quality-lib && bunx code-quality
84
-
85
- # Or with yarn
86
- yarn add -D code-quality-lib && yarn code-quality
87
- ```
88
-
89
- ## Usage
90
-
91
- ### As a CLI Tool
92
-
93
- ```bash
94
- # Run all quality checks
95
- code-quality
96
-
97
- # Run with detailed error logs in terminal
98
- code-quality --logs
99
-
100
- # Or use with npx (without installing)
101
- npx code-quality-lib
102
-
103
- # Or with bunx (without installing)
104
- bunx code-quality-lib
105
-
106
- # Or with yarn
107
- yarn code-quality
32
+ npx code-quality # npm
33
+ bunx code-quality # bun
34
+ pnpm dlx code-quality # pnpm
35
+ yarn dlx code-quality # yarn
108
36
  ```
109
37
 
110
- ### Error Reporting
111
-
112
- The library automatically generates a detailed error report at `.quality-report.md` with:
113
- - ✅ Status of each quality check
114
- - 📋 Full error output for failed checks
115
- - 💡 Suggestions for fixing common issues
116
- - 🤖 AI-friendly structured information
38
+ ## CLI Usage
117
39
 
118
- **Viewing Errors:**
119
40
  ```bash
120
- # Silent mode (default) - errors saved to .quality-report.md
121
- code-quality
122
-
123
- # Verbose mode - errors shown in terminal + saved to report
124
- code-quality --logs
125
-
126
- # View the report
127
- cat .quality-report.md
41
+ code-quality # run all quality checks
42
+ code-quality --logs # show detailed error output
43
+ code-quality --help # show help
44
+ code-quality --version # show version
128
45
  ```
129
46
 
130
- **Note:** Add `.quality-report.md` to your `.gitignore` to keep reports local only.
131
-
132
- ### As a Library
47
+ ## Library Usage
133
48
 
134
49
  ```javascript
135
- const { CodeQualityChecker } = require('code-quality-lib');
136
-
137
- // Use default configuration
138
- const checker = new CodeQualityChecker();
139
- checker.run().then(result => {
140
- console.log(result.success ? '✅ All checks passed!' : '❌ Some checks failed');
141
- });
142
- ```
143
-
144
- ## Configuration
145
-
146
- ### Default Tools
147
-
148
- The library runs these tools by default:
149
- - **TypeScript** - Type checking and compilation
150
- - **ESLint** - Code linting and style checking
151
- - **Prettier** - Code formatting validation
152
- - **Knip** - Dead code detection and unused exports
153
- - **Snyk** - Security vulnerability scanning
50
+ const { CodeQualityChecker, runQualityCheck } = require('code-quality-lib');
154
51
 
155
- ### Custom Configuration
52
+ // Quick — run all checks with defaults
53
+ const result = await runQualityCheck();
54
+ console.log(result.success ? 'All passed' : 'Some failed');
156
55
 
157
- ```javascript
158
- const customChecker = new CodeQualityChecker({
159
- // Force specific package manager
160
- packageManager: 'pnpm', // 'bun' | 'pnpm' | 'yarn' | 'npm'
161
-
162
- // Only run specific tools
56
+ // Custom — select tools, override commands
57
+ const checker = new CodeQualityChecker({
163
58
  tools: ['TypeScript', 'ESLint'],
164
-
165
- // Custom commands for each tool
59
+ packageManager: 'pnpm',
166
60
  commands: {
167
61
  TypeScript: 'tsc --noEmit',
168
62
  ESLint: 'eslint src/ --ext .ts,.tsx',
169
- Prettier: 'prettier --check "src/**/*.{ts,tsx}"'
170
63
  },
171
-
172
- // Custom descriptions
173
- descriptions: {
174
- TypeScript: 'TypeScript type checking',
175
- ESLint: 'ESLint code analysis'
176
- },
177
-
178
- // Disable .env loading
179
- loadEnv: false
64
+ loadEnv: false,
180
65
  });
181
- ```
182
66
 
183
- ### Package Manager Detection
184
-
185
- The library automatically detects your package manager in this order:
186
- 1. **Lock files**: `bun.lock`, `pnpm-lock.yaml`, `yarn.lock`, `package-lock.json`
187
- 2. **Available commands**: Checks if `bun`, `pnpm`, `yarn` are installed
188
- 3. **Fallback**: Uses `npm` if nothing else is found
189
-
190
- You can also override the detection:
191
- ```javascript
192
- const checker = new CodeQualityChecker({
193
- packageManager: 'yarn' // Force yarn usage
194
- });
67
+ const result = await checker.run({ showLogs: true });
68
+ console.log(result.results); // per-tool results array
195
69
  ```
196
70
 
71
+ ## Configuration Options
197
72
 
73
+ | Option | Type | Default | Description |
74
+ |--------|------|---------|-------------|
75
+ | `tools` | `string[]` | All 5 tools | Which tools to run |
76
+ | `packageManager` | `'npm' \| 'bun' \| 'pnpm' \| 'yarn'` | auto-detected | Force a specific package manager |
77
+ | `commands` | `Record<string, string>` | bundled paths | Custom commands per tool |
78
+ | `descriptions` | `Record<string, string>` | built-in | Custom descriptions per tool |
79
+ | `loadEnv` | `boolean` | `true` | Load `.env` file |
198
80
 
199
- ## API Reference
200
-
201
- ### CodeQualityChecker
202
-
203
- #### Constructor
204
- ```javascript
205
- new CodeQualityChecker(options)
206
- ```
207
-
208
- #### Options
209
- - `loadEnv` (boolean): Load environment variables from `.env` file
210
- - `tools` (string[]): Array of tool names to run
211
- - `commands` (Record<string, string>): Custom commands for each tool
212
- - `descriptions` (Record<string, string>): Descriptions shown during execution
213
- - `packageManager` ('bun' | 'pnpm' | 'yarn' | 'npm'): Force specific package manager (auto-detected if not specified)
214
-
215
- #### Methods
216
- - `run()`: Promise<QualityCheckResult> - Run all configured checks
217
- - `runCommand(command, description)`: CommandResult - Execute a single command
218
- - `formatOutput(tool, result)`: string - Format output for a tool
219
- - `checkSnykToken()`: boolean - Check Snyk authentication status
220
-
221
- ### Types
222
-
223
- ```typescript
224
- interface CodeQualityOptions {
225
- loadEnv?: boolean;
226
- tools?: string[];
227
- commands?: Record<string, string>;
228
- descriptions?: Record<string, string>;
229
- packageManager?: 'bun' | 'pnpm' | 'yarn' | 'npm';
230
- }
231
-
232
- interface CommandResult {
233
- success: boolean;
234
- output: string;
235
- }
236
-
237
- interface QualityCheckResult {
238
- success: boolean;
239
- message: string;
240
- }
241
- ```
81
+ ## Bundled Tools
242
82
 
243
- ## Requirements
83
+ All tools are included as dependencies — zero extra setup:
244
84
 
245
- - **Node.js** >= 18.0.0 (tested on 25.x, 22.x, 20.x)
246
- - **Package Manager**: bun, pnpm, yarn, or npm (auto-detected)
247
- - **Quality Tools** (install only what you need):
248
- - **TypeScript** - `npm install -D typescript` or `bun add -D typescript` or `pnpm add -D typescript` or `yarn add -D typescript`
249
- - **ESLint** - `npm install -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin` or `bun add -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin`
250
- - **Prettier** - `npm install -D prettier eslint-config-prettier` or `bun add -D prettier eslint-config-prettier`
251
- - **Knip** - `npm install -D knip` or `bun add -D knip`
252
- - **Snyk** - `npm install -D snyk` or `bun add -D snyk`
85
+ | Tool | Description |
86
+ |------|-------------|
87
+ | **TypeScript** | Type checking (`tsc --noEmit`) |
88
+ | **ESLint** | Linting with plugins (react, sonarjs, unicorn, import, prettier) |
89
+ | **Prettier** | Code formatting validation |
90
+ | **Knip** | Dead code and unused export detection |
91
+ | **Snyk** | Security vulnerability scanning |
253
92
 
254
- ### Quick Setup
93
+ ## Package Manager Detection
255
94
 
256
- #### npm
257
- ```bash
258
- # Install all quality tools
259
- npm install -D typescript eslint prettier knip snyk
95
+ Automatically detected by lock file presence:
260
96
 
261
- # Minimal setup
262
- npm install -D typescript eslint prettier
263
- ```
97
+ 1. `bun.lock` / `bun.lockb` → bun
98
+ 2. `pnpm-lock.yaml` pnpm
99
+ 3. `yarn.lock` → yarn
100
+ 4. `package-lock.json` → npm
101
+ 5. Fallback: checks installed binaries, defaults to npm
264
102
 
265
- #### bun (recommended - faster)
266
- ```bash
267
- # Install all quality tools
268
- bun add -D typescript eslint prettier knip snyk
103
+ ## Error Reporting
269
104
 
270
- # Minimal setup
271
- bun add -D typescript eslint prettier
272
- ```
105
+ Every run generates `.quality-report.md` with:
106
+ - Status of each check (pass/fail)
107
+ - Full error output for failed checks
108
+ - AI-friendly structured information for automated fixes
273
109
 
274
- #### pnpm
275
- ```bash
276
- # Install all quality tools
277
- pnpm add -D typescript eslint prettier knip snyk
110
+ Add `.quality-report.md` to your `.gitignore`.
278
111
 
279
- # Minimal setup
280
- pnpm add -D typescript eslint prettier
281
- ```
112
+ ## AI Skills
282
113
 
283
- #### yarn
284
- ```bash
285
- # Install all quality tools
286
- yarn add -D typescript eslint prettier knip snyk
114
+ This library includes `.ai/skills/` — markdown files that teach AI coding assistants (Cursor, Copilot, Windsurf, etc.) to follow the project's coding standards. See [`.ai/skills/README.md`](.ai/skills/README.md).
287
115
 
288
- # Minimal setup
289
- yarn add -D typescript eslint prettier
290
- ```
116
+ ## Requirements
291
117
 
292
- **Note**: The library automatically skips tools that aren't installed, so you can start with just the tools you need and add more later.
118
+ - **Node.js** >= 18.0.0
293
119
 
294
- ## Framework Support
120
+ ## Testing & CI/CD
295
121
 
296
- Works with any Node.js framework: Next.js, React, Vue, Angular, Express, etc.
122
+ Tested on every push across 4 runtimes:
123
+ - **Node.js 25.x** (npm)
124
+ - **Bun 1.3.x**
125
+ - **pnpm 10.x**
126
+ - **Yarn 4.13.0**
297
127
 
298
128
  ## Contributing
299
129
 
300
- Contributions welcome! Fork, create a feature branch, and submit a PR.
130
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and guidelines.
301
131
 
302
132
  ## License
303
133
 
304
- This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
305
-
306
- ## Testing & CI/CD
307
-
308
- This library is automatically tested across multiple runtimes:
309
- - **Node.js** 25.x (npm)
310
- - **Bun** 1.3.x
311
- - **pnpm** 10.x
312
- - **Yarn** 4.13.0
313
-
314
- All tests run in parallel on every push and pull request. The library only publishes to npm when all tests pass.
315
-
134
+ MIT see [LICENSE](LICENSE).
package/index.d.ts CHANGED
@@ -1,9 +1,16 @@
1
1
  export interface CodeQualityOptions {
2
+ /** Load environment variables from .env file (default: true) */
2
3
  loadEnv?: boolean;
4
+ /** Array of tool names to run (default: all tools) */
3
5
  tools?: string[];
6
+ /** Custom commands for each tool, keyed by tool name */
4
7
  commands?: Record<string, string>;
8
+ /** Custom descriptions for each tool, keyed by tool name */
5
9
  descriptions?: Record<string, string>;
10
+ /** Force a specific package manager (auto-detected if not specified) */
6
11
  packageManager?: 'bun' | 'pnpm' | 'yarn' | 'npm';
12
+ /** Show detailed error logs in terminal */
13
+ showLogs?: boolean;
7
14
  }
8
15
 
9
16
  export interface CommandResult {
@@ -11,21 +18,30 @@ export interface CommandResult {
11
18
  output: string;
12
19
  }
13
20
 
21
+ export interface CheckResult {
22
+ name: string;
23
+ description: string;
24
+ success: boolean;
25
+ output: string;
26
+ }
27
+
14
28
  export interface QualityCheckResult {
15
29
  success: boolean;
16
30
  message: string;
31
+ results: CheckResult[];
17
32
  }
18
33
 
19
34
  export class CodeQualityChecker {
35
+ options: Required<Omit<CodeQualityOptions, 'showLogs'>>;
36
+
20
37
  constructor(options?: CodeQualityOptions);
21
-
38
+
39
+ /** Execute a single shell command and return the result */
22
40
  runCommand(command: string, description: string): CommandResult;
23
-
24
- formatOutput(tool: string, result: CommandResult): string;
25
-
26
- checkSnykToken(): boolean;
27
-
28
- run(): Promise<QualityCheckResult>;
41
+
42
+ /** Run all configured quality checks */
43
+ run(options?: { showLogs?: boolean }): Promise<QualityCheckResult>;
29
44
  }
30
45
 
46
+ /** Convenience function to run quality checks without instantiating the class */
31
47
  export function runQualityCheck(options?: CodeQualityOptions): Promise<QualityCheckResult>;
package/index.js CHANGED
@@ -4,184 +4,289 @@ 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');
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
- const envContent = fs.readFileSync(envPath, 'utf8');
17
- const lines = envContent.split('\n');
18
-
19
- lines.forEach(line => {
20
- const trimmed = line.trim();
21
- if (trimmed && !trimmed.startsWith('#')) {
22
- const [key, ...valueParts] = trimmed.split('=');
23
- if (key && valueParts.length > 0) {
24
- process.env[key] = valueParts.join('=');
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 (error) {
32
- console.log('⚠️ Could not load .env file:', error.message);
63
+ } catch {
64
+ // silently continue without .env
33
65
  }
34
66
  }
35
67
 
36
- loadEnvFile();
68
+ // ─── Tool Path Resolution ───────────────────────────────────────────────────
37
69
 
38
- console.log('\n🔍 Professional Code Quality Check\n');
39
- console.log('─'.repeat(50));
40
- console.log('📦 Using bun package manager\n');
41
-
42
- if (showLogs) {
43
- console.log('📋 Detailed error logging enabled (--logs flag)\n');
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
- // 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**: bun\n\n`;
52
- reportContent += `---\n\n`;
53
-
54
- // Run quality checks
55
- const checks = [
56
- { name: 'TypeScript', cmd: 'npx tsc --noEmit', description: 'Type checking and compilation' },
57
- { name: 'ESLint', cmd: 'npx eslint . --ext .js,.jsx,.ts,.tsx', description: 'Code linting and style checking' },
58
- { name: 'Prettier', cmd: 'npx prettier --check .', description: 'Code formatting validation' },
59
- { name: 'Knip', cmd: 'npx knip', description: 'Dead code detection' },
60
- { name: 'Snyk', cmd: 'npx snyk test --severity-threshold=high', description: 'Security vulnerability scanning' }
82
+ // ─── Default Checks ─────────────────────────────────────────────────────────
83
+
84
+ const DEFAULT_TOOLS = [
85
+ { name: 'TypeScript', bin: 'tsc', args: '--noEmit', description: 'Type checking and compilation' },
86
+ { name: 'ESLint', bin: 'eslint', args: '. --ext .js,.jsx,.ts,.tsx', description: 'Code linting and style checking' },
87
+ { name: 'Prettier', bin: 'prettier', args: '--check .', description: 'Code formatting validation' },
88
+ { name: 'Knip', bin: 'knip', args: '', description: 'Dead code detection' },
89
+ { name: 'Snyk', bin: 'snyk', args: 'test --severity-threshold=high', description: 'Security vulnerability scanning' },
61
90
  ];
62
91
 
63
- let allPassed = true;
64
- const results = [];
92
+ // ─── CodeQualityChecker Class ───────────────────────────────────────────────
65
93
 
66
- checks.forEach(({ name, cmd, description }) => {
67
- console.log(`Running ${name}...`);
68
-
69
- reportContent += `## ${name}\n\n`;
70
- reportContent += `**Description**: ${description}\n\n`;
71
- reportContent += `**Command**: \`${cmd}\`\n\n`;
72
-
73
- try {
74
- const output = execSync(cmd, { stdio: 'pipe', encoding: 'utf8' });
75
- console.log(`✅ ${name}: Passed`);
76
-
77
- reportContent += `**Status**: ✅ **PASSED**\n\n`;
78
- if (output && output.trim()) {
79
- reportContent += `**Output**:\n\`\`\`\n${output.trim()}\n\`\`\`\n\n`;
80
-
81
- if (showLogs) {
82
- console.log(`\n📄 ${name} Output:`);
83
- console.log(output.trim());
94
+ class CodeQualityChecker {
95
+ constructor(options = {}) {
96
+ this.options = {
97
+ loadEnv: options.loadEnv !== false,
98
+ tools: options.tools || DEFAULT_TOOLS.map((t) => t.name),
99
+ commands: options.commands || {},
100
+ descriptions: options.descriptions || {},
101
+ packageManager: options.packageManager || detectPackageManager(),
102
+ };
103
+
104
+ if (this.options.loadEnv) loadEnvFile();
105
+ }
106
+
107
+ _getChecks() {
108
+ const binDir = resolveToolBinDir();
109
+ const checks = [];
110
+
111
+ for (const toolName of this.options.tools) {
112
+ const defaultTool = DEFAULT_TOOLS.find((t) => t.name === toolName);
113
+ if (!defaultTool && !this.options.commands[toolName]) continue;
114
+
115
+ const cmd =
116
+ this.options.commands[toolName] ||
117
+ `${path.join(binDir, defaultTool.bin)}${defaultTool.args ? ' ' + defaultTool.args : ''}`;
118
+
119
+ const description =
120
+ this.options.descriptions[toolName] ||
121
+ (defaultTool ? defaultTool.description : toolName);
122
+
123
+ checks.push({ name: toolName, cmd, description });
124
+ }
125
+
126
+ return checks;
127
+ }
128
+
129
+ runCommand(command, description) {
130
+ try {
131
+ const output = execSync(command, { stdio: 'pipe', encoding: 'utf8' });
132
+ return { success: true, output: (output || '').trim() };
133
+ } catch (error) {
134
+ const output = error.stdout || error.stderr || error.message || 'Unknown error';
135
+ return { success: false, output: output.trim() };
136
+ }
137
+ }
138
+
139
+ async run(options = {}) {
140
+ const showLogs = options.showLogs || false;
141
+ const checks = this._getChecks();
142
+ const pm = this.options.packageManager;
143
+ const runCmd = getRunPrefix(pm);
144
+ const results = [];
145
+ let allPassed = true;
146
+
147
+ console.log('\n� Professional Code Quality Check\n');
148
+ console.log('─'.repeat(50));
149
+ console.log(`📦 Using ${pm} package manager\n`);
150
+
151
+ if (showLogs) {
152
+ console.log('📋 Detailed error logging enabled (--logs flag)\n');
153
+ }
154
+
155
+ for (const { name, cmd, description } of checks) {
156
+ console.log(`Running ${name}...`);
157
+ const result = this.runCommand(cmd, description);
158
+
159
+ if (result.success) {
160
+ console.log(`✅ ${name}: Passed`);
161
+ } else {
162
+ allPassed = false;
163
+ console.log(`❌ ${name}: Failed`);
164
+ }
165
+
166
+ if (showLogs && result.output) {
167
+ const icon = result.success ? '📄' : '❌';
168
+ console.log(`\n${icon} ${name} ${result.success ? 'Output' : 'Error Details'}:`);
169
+ console.log('─'.repeat(50));
170
+ console.log(result.output);
171
+ console.log('─'.repeat(50));
84
172
  console.log('');
85
173
  }
174
+
175
+ results.push({ name, description, ...result });
86
176
  }
87
-
88
- results.push({ name, status: 'passed', output: output.trim() });
89
- } catch (error) {
90
- allPassed = false;
91
- const errorOutput = error.stdout || error.stderr || error.message || 'Unknown error';
92
- console.log(`❌ ${name}: Failed`);
93
-
94
- // Show errors in terminal if --logs flag is present
95
- if (showLogs) {
96
- console.log(`\n❌ ${name} Error Details:`);
97
- console.log('─'.repeat(50));
98
- console.log(errorOutput.trim());
99
- console.log('─'.repeat(50));
100
- console.log('');
177
+
178
+ // Generate report
179
+ this._writeReport(results, allPassed, pm, runCmd);
180
+
181
+ console.log('\n' + ''.repeat(50));
182
+
183
+ if (allPassed) {
184
+ console.log('\n🎉 All quality checks passed! Code is ready for production.\n');
185
+ } else {
186
+ console.log('\n❌ Some quality checks failed. Please fix the issues above.\n');
187
+ if (!showLogs) {
188
+ console.log('💡 Run with --logs flag to see detailed errors in terminal');
189
+ }
190
+ console.log('📄 See .quality-report.md for detailed error information\n');
101
191
  }
102
-
103
- reportContent += `**Status**: ❌ **FAILED**\n\n`;
104
- reportContent += `**Error Output**:\n\`\`\`\n${errorOutput.trim()}\n\`\`\`\n\n`;
105
-
106
- // Add suggestions for common issues
107
- reportContent += `**Suggestions**:\n`;
108
- if (name === 'TypeScript') {
109
- reportContent += `- Check type errors in the output above\n`;
110
- reportContent += `- Run \`npx tsc --noEmit\` to see detailed errors\n`;
111
- reportContent += `- Fix type mismatches and missing type definitions\n`;
112
- } else if (name === 'ESLint') {
113
- reportContent += `- Run \`bun run lint:fix\` to auto-fix issues\n`;
114
- reportContent += `- Check ESLint errors in the output above\n`;
115
- reportContent += `- Review and fix code style violations\n`;
116
- } else if (name === 'Prettier') {
117
- reportContent += `- Run \`bun run format:fix\` to auto-format files\n`;
118
- reportContent += `- Check formatting issues in the output above\n`;
119
- } else if (name === 'Knip') {
120
- reportContent += `- Remove unused exports and dependencies\n`;
121
- reportContent += `- Check dead code in the output above\n`;
122
- } else if (name === 'Snyk') {
123
- reportContent += `- Review security vulnerabilities in the output above\n`;
124
- reportContent += `- Update vulnerable dependencies\n`;
125
- reportContent += `- Run \`npx snyk wizard\` for guided fixes\n`;
192
+
193
+ return {
194
+ success: allPassed,
195
+ message: allPassed ? 'All quality checks passed' : 'Some quality checks failed',
196
+ results,
197
+ };
198
+ }
199
+
200
+ _writeReport(results, allPassed, pm, runCmd) {
201
+ const reportPath = path.join(process.cwd(), '.quality-report.md');
202
+ const timestamp = new Date().toISOString();
203
+ const passed = results.filter((r) => r.success);
204
+ const failed = results.filter((r) => !r.success);
205
+
206
+ let report = `# Code Quality Report\n\n`;
207
+ report += `**Generated**: ${timestamp}\n`;
208
+ report += `**Package Manager**: ${pm}\n\n`;
209
+ report += `---\n\n`;
210
+
211
+ for (const r of results) {
212
+ report += `## ${r.name}\n\n`;
213
+ report += `**Description**: ${r.description}\n\n`;
214
+ report += `**Status**: ${r.success ? '✅ **PASSED**' : '❌ **FAILED**'}\n\n`;
215
+ if (r.output) {
216
+ report += `**Output**:\n\`\`\`\n${r.output}\n\`\`\`\n\n`;
217
+ }
218
+ report += `---\n\n`;
219
+ }
220
+
221
+ report += `## Summary\n\n`;
222
+ report += `**Total Checks**: ${results.length}\n`;
223
+ report += `**Passed**: ${passed.length}\n`;
224
+ report += `**Failed**: ${failed.length}\n\n`;
225
+
226
+ if (allPassed) {
227
+ report += `### ✅ All quality checks passed!\n\nYour code is ready for production.\n\n`;
228
+ } else {
229
+ report += `### ❌ Some quality checks failed\n\n`;
230
+ report += `**Quick Fix Commands**:\n`;
231
+ report += `- \`${runCmd} lint:fix\` — Auto-fix linting issues\n`;
232
+ report += `- \`${runCmd} format:fix\` — Auto-format code\n\n`;
233
+ }
234
+
235
+ report += `---\n\n## For AI Agents\n\n`;
236
+ report += `**Failed Checks**: ${failed.map((r) => r.name).join(', ') || 'None'}\n\n`;
237
+ if (!allPassed) {
238
+ report += `**Action Required**:\n`;
239
+ for (const r of failed) {
240
+ report += `- Fix ${r.name} errors\n`;
241
+ }
242
+ report += `\n`;
243
+ }
244
+
245
+ try {
246
+ fs.writeFileSync(reportPath, report, 'utf8');
247
+ console.log(`\n📄 Quality report saved to: .quality-report.md`);
248
+ } catch (err) {
249
+ console.error(`\n⚠️ Failed to write report: ${err.message}`);
126
250
  }
127
- reportContent += `\n`;
128
-
129
- results.push({ name, status: 'failed', error: errorOutput.trim() });
130
251
  }
131
-
132
- reportContent += `---\n\n`;
133
- });
134
-
135
- // Add summary
136
- reportContent += `## Summary\n\n`;
137
- reportContent += `**Total Checks**: ${checks.length}\n`;
138
- reportContent += `**Passed**: ${results.filter(r => r.status === 'passed').length}\n`;
139
- reportContent += `**Failed**: ${results.filter(r => r.status === 'failed').length}\n\n`;
140
-
141
- if (allPassed) {
142
- reportContent += `### ✅ All quality checks passed!\n\n`;
143
- reportContent += `Your code is ready for production.\n\n`;
144
- } else {
145
- reportContent += `### ❌ Some quality checks failed\n\n`;
146
- reportContent += `Please review the errors above and fix the issues.\n\n`;
147
- reportContent += `**Quick Fix Commands**:\n`;
148
- reportContent += `- \`bun run fix\` - Auto-fix linting and formatting\n`;
149
- reportContent += `- \`bun run type:check\` - Check TypeScript errors\n`;
150
- reportContent += `- \`bun run lint:check\` - Check ESLint errors\n`;
151
- reportContent += `- \`bun run format:check\` - Check Prettier formatting\n\n`;
152
252
  }
153
253
 
154
- // Add AI Agent section
155
- reportContent += `---\n\n`;
156
- reportContent += `## For AI Agents\n\n`;
157
- reportContent += `This report contains detailed error information for automated code quality fixes.\n\n`;
158
- reportContent += `**Failed Checks**: ${results.filter(r => r.status === 'failed').map(r => r.name).join(', ') || 'None'}\n\n`;
159
- if (!allPassed) {
160
- reportContent += `**Action Required**:\n`;
161
- results.filter(r => r.status === 'failed').forEach(r => {
162
- reportContent += `- Fix ${r.name} errors\n`;
163
- });
164
- reportContent += `\n`;
165
- }
254
+ // ─── Convenience Function ───────────────────────────────────────────────────
166
255
 
167
- // Write report to file (overwrite existing)
168
- try {
169
- fs.writeFileSync(reportPath, reportContent, 'utf8');
170
- console.log(`\n📄 Quality report saved to: .quality-report.md`);
171
- } catch (error) {
172
- console.error(`\n⚠️ Failed to write report: ${error.message}`);
256
+ async function runQualityCheck(options = {}) {
257
+ const checker = new CodeQualityChecker(options);
258
+ return checker.run({ showLogs: options.showLogs || false });
173
259
  }
174
260
 
175
- console.log('\n' + '─'.repeat(50));
261
+ // ─── CLI Entry Point ────────────────────────────────────────────────────────
262
+
263
+ if (require.main === module) {
264
+ const args = process.argv.slice(2);
265
+
266
+ if (args.includes('--help') || args.includes('-h')) {
267
+ console.log('Usage: code-quality [options]');
268
+ console.log('');
269
+ console.log('Options:');
270
+ console.log(' --help, -h Show this help message');
271
+ console.log(' --version, -v Show version number');
272
+ console.log(' --logs Show detailed error logs');
273
+ console.log('');
274
+ console.log('Runs TypeScript, ESLint, Prettier, Knip, and Snyk checks.');
275
+ process.exit(0);
276
+ }
176
277
 
177
- if (allPassed) {
178
- console.log('\n🎉 All quality checks passed! Code is ready for production.\n');
179
- process.exit(0);
180
- } else {
181
- console.log('\n❌ Some quality checks failed. Please fix the issues above.\n');
182
- if (!showLogs) {
183
- console.log('💡 Run with --logs flag to see detailed errors in terminal');
278
+ if (args.includes('--version') || args.includes('-v')) {
279
+ const pkg = require('./package.json');
280
+ console.log(pkg.version);
281
+ process.exit(0);
184
282
  }
185
- console.log('📄 See .quality-report.md for detailed error information\n');
186
- process.exit(1);
283
+
284
+ const checker = new CodeQualityChecker();
285
+ checker.run({ showLogs: args.includes('--logs') }).then((result) => {
286
+ process.exit(result.success ? 0 : 1);
287
+ });
187
288
  }
289
+
290
+ // ─── Exports ────────────────────────────────────────────────────────────────
291
+
292
+ module.exports = { CodeQualityChecker, runQualityCheck };
package/package.json CHANGED
@@ -1,19 +1,16 @@
1
1
  {
2
2
  "name": "code-quality-lib",
3
- "version": "1.1.0",
3
+ "version": "2.0.1",
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
- "lint": "echo 'Linting not configured yet'",
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,22 +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
  },
30
+ "dependencies": {
31
+ "@typescript-eslint/eslint-plugin": "^8.20.0",
32
+ "@typescript-eslint/parser": "^8.20.0",
33
+ "eslint": "^9.18.0",
34
+ "eslint-config-prettier": "^9.1.0",
35
+ "eslint-plugin-import": "^2.31.0",
36
+ "eslint-plugin-prettier": "^5.2.1",
37
+ "eslint-plugin-react": "^7.37.2",
38
+ "eslint-plugin-react-hooks": "^5.1.0",
39
+ "eslint-plugin-react-refresh": "^0.4.16",
40
+ "eslint-plugin-sonarjs": "^4.0.2",
41
+ "eslint-plugin-storybook": "^0.11.1",
42
+ "eslint-plugin-unicorn": "^57.0.0",
43
+ "knip": "^5.43.2",
44
+ "prettier": "^3.4.2",
45
+ "snyk": "^1.1293.1",
46
+ "typescript": "^5.8.3"
47
+ },
33
48
  "files": [
34
49
  "index.js",
35
50
  "index.d.ts",
36
51
  "README.md",
37
- "LICENSE",
38
- ".eslintrc.js",
39
- ".prettierrc",
40
- "knip.json",
41
- "tsconfig.json",
42
- ".code-quality.json.example"
52
+ "LICENSE"
43
53
  ],
44
54
  "repository": {
45
55
  "type": "git",
@@ -1,8 +0,0 @@
1
- {
2
- "useLibraryRules": true,
3
- "useProjectRules": false,
4
- "customSetup": false,
5
- "tools": ["TypeScript", "ESLint", "Prettier", "Knip", "Snyk"],
6
- "copyConfigs": true,
7
- "version": "1.0.1"
8
- }
package/.eslintrc.js DELETED
@@ -1,121 +0,0 @@
1
- module.exports = {
2
- root: true,
3
- env: {
4
- browser: true,
5
- es2022: true,
6
- node: true,
7
- },
8
- extends: [
9
- 'eslint:recommended',
10
- '@typescript-eslint/recommended',
11
- '@typescript-eslint/recommended-requiring-type-checking',
12
- 'plugin:react/recommended',
13
- 'plugin:react-hooks/recommended',
14
- 'plugin:storybook/recommended',
15
- 'prettier',
16
- ],
17
- parser: '@typescript-eslint/parser',
18
- parserOptions: {
19
- ecmaFeatures: {
20
- jsx: true,
21
- },
22
- ecmaVersion: 'latest',
23
- sourceType: 'module',
24
- project: ['./tsconfig.json', './tsconfig.node.json'],
25
- tsconfigRootDir: __dirname,
26
- },
27
- plugins: [
28
- 'react',
29
- 'react-hooks',
30
- 'react-refresh',
31
- '@typescript-eslint',
32
- 'sonarjs',
33
- 'unicorn',
34
- 'import',
35
- 'prettier',
36
- ],
37
- rules: {
38
- // TypeScript
39
- '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
40
- '@typescript-eslint/no-explicit-any': 'warn',
41
- '@typescript-eslint/prefer-nullish-coalescing': 'error',
42
- '@typescript-eslint/prefer-optional-chain': 'error',
43
- '@typescript-eslint/no-non-null-assertion': 'warn',
44
-
45
- // React
46
- 'react/react-in-jsx-scope': 'off',
47
- 'react/prop-types': 'off',
48
- 'react-hooks/rules-of-hooks': 'error',
49
- 'react-hooks/exhaustive-deps': 'warn',
50
- 'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
51
-
52
- // Code Quality
53
- 'sonarjs/cognitive-complexity': ['warn', 15],
54
- 'sonarjs/no-duplicate-string': 'warn',
55
- 'sonarjs/no-identical-functions': 'warn',
56
-
57
- // Best Practices
58
- 'unicorn/no-array-for-each': 'error',
59
- 'unicorn/prefer-array-some': 'error',
60
- 'unicorn/no-new-array': 'error',
61
- 'unicorn/prefer-node-protocol': 'error',
62
-
63
- // Import/Export
64
- 'import/order': [
65
- 'error',
66
- {
67
- groups: [
68
- 'builtin',
69
- 'external',
70
- 'internal',
71
- 'parent',
72
- 'sibling',
73
- 'index',
74
- ],
75
- 'newlines-between': 'always',
76
- alphabetize: {
77
- order: 'asc',
78
- caseInsensitive: true,
79
- },
80
- },
81
- ],
82
-
83
- // Prettier (conflicts handled by eslint-config-prettier)
84
- 'prettier/prettier': 'error',
85
-
86
- // General
87
- 'no-console': ['warn', { allow: ['warn', 'error'] }],
88
- 'no-debugger': 'error',
89
- 'prefer-const': 'error',
90
- 'no-var': 'error',
91
- },
92
- settings: {
93
- react: {
94
- version: 'detect',
95
- },
96
- 'import/resolver': {
97
- typescript: {
98
- alwaysTryTypes: true,
99
- },
100
- },
101
- },
102
- overrides: [
103
- {
104
- files: ['**/*.test.ts', '**/*.test.tsx', '**/*.spec.ts', '**/*.spec.tsx'],
105
- env: {
106
- jest: true,
107
- },
108
- rules: {
109
- '@typescript-eslint/no-explicit-any': 'off',
110
- 'sonarjs/no-duplicate-string': 'off',
111
- },
112
- },
113
- {
114
- files: ['**/*.stories.@(js|jsx|ts|tsx|mdx)'],
115
- rules: {
116
- 'import/no-extraneous-dependencies': 'off',
117
- 'react/prop-types': 'off',
118
- },
119
- },
120
- ],
121
- };
package/.prettierrc DELETED
@@ -1,17 +0,0 @@
1
- {
2
- "semi": false,
3
- "trailingComma": "es5",
4
- "singleQuote": true,
5
- "printWidth": 100,
6
- "tabWidth": 2,
7
- "useTabs": false,
8
- "endOfLine": "lf",
9
- "arrowParens": "always",
10
- "bracketSpacing": true,
11
- "bracketSameLine": false,
12
- "quoteProps": "as-needed",
13
- "jsxSingleQuote": true,
14
- "proseWrap": "preserve",
15
- "htmlWhitespaceSensitivity": "css",
16
- "embeddedLanguageFormatting": "auto"
17
- }
package/knip.json DELETED
@@ -1,76 +0,0 @@
1
- {
2
- "entry": ["index.js", "index.d.ts"],
3
- "project": ["package.json"],
4
- "ignore": [
5
- "node_modules",
6
- "dist",
7
- "build",
8
- "coverage",
9
- "*.config.js",
10
- "*.config.ts",
11
- "test/**",
12
- "**/*.test.*",
13
- "**/*.spec.*",
14
- "examples/**"
15
- ],
16
- "ignoreBinaries": ["code-quality"],
17
- "ignoreDependencies": [
18
- "@types/*",
19
- "eslint",
20
- "prettier",
21
- "typescript",
22
- "vitest",
23
- "storybook"
24
- ],
25
- "rules": {
26
- "dependencies": {
27
- "entries": {
28
- "entryFiles": ["index.js", "index.d.ts"],
29
- "ignore": []
30
- },
31
- "exports": {
32
- "ignore": []
33
- },
34
- "files": {
35
- "ignore": [
36
- "test/**",
37
- "**/*.test.*",
38
- "**/*.spec.*",
39
- "examples/**"
40
- ]
41
- },
42
- "classMembers": {
43
- "ignore": []
44
- },
45
- "enums": {
46
- "ignore": []
47
- },
48
- "enumMembers": {
49
- "ignore": []
50
- },
51
- "types": {
52
- "ignore": []
53
- }
54
- },
55
- "exports": {
56
- "ignore": []
57
- },
58
- "files": {
59
- "ignore": [
60
- "test/**",
61
- "**/*.test.*",
62
- "**/*.spec.*",
63
- "examples/**"
64
- ]
65
- },
66
- "duplicates": {
67
- "ignore": []
68
- },
69
- "unlisted": {
70
- "ignore": []
71
- },
72
- "unresolved": {
73
- "ignore": []
74
- }
75
- }
76
- }
package/tsconfig.json DELETED
@@ -1,41 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "lib": ["ES2022"],
5
- "allowJs": true,
6
- "skipLibCheck": true,
7
- "esModuleInterop": true,
8
- "allowSyntheticDefaultImports": true,
9
- "strict": true,
10
- "forceConsistentCasingInFileNames": true,
11
- "noFallthroughCasesInSwitch": true,
12
- "module": "ESNext",
13
- "moduleResolution": "bundler",
14
- "resolveJsonModule": true,
15
- "isolatedModules": true,
16
- "noEmit": true,
17
- "declaration": true,
18
- "declarationMap": true,
19
- "outDir": "dist",
20
- "rootDir": ".",
21
- "jsx": "react-jsx",
22
- "incremental": true,
23
- "noUncheckedIndexedAccess": true,
24
- "exactOptionalPropertyTypes": true,
25
- "noImplicitReturns": true,
26
- "noImplicitOverride": true,
27
- "noPropertyAccessFromIndexSignature": false
28
- },
29
- "include": [
30
- "index.js",
31
- "index.d.ts",
32
- "test/**/*"
33
- ],
34
- "exclude": [
35
- "node_modules",
36
- "dist",
37
- "build",
38
- "coverage",
39
- "examples"
40
- ]
41
- }