projscan 0.1.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.
- package/LICENSE +21 -0
- package/README.md +193 -0
- package/dist/analyzers/architectureCheck.d.ts +2 -0
- package/dist/analyzers/architectureCheck.js +91 -0
- package/dist/analyzers/architectureCheck.js.map +1 -0
- package/dist/analyzers/dependencyRiskCheck.d.ts +2 -0
- package/dist/analyzers/dependencyRiskCheck.js +17 -0
- package/dist/analyzers/dependencyRiskCheck.js.map +1 -0
- package/dist/analyzers/eslintCheck.d.ts +2 -0
- package/dist/analyzers/eslintCheck.js +49 -0
- package/dist/analyzers/eslintCheck.js.map +1 -0
- package/dist/analyzers/prettierCheck.d.ts +2 -0
- package/dist/analyzers/prettierCheck.js +53 -0
- package/dist/analyzers/prettierCheck.js.map +1 -0
- package/dist/analyzers/testCheck.d.ts +2 -0
- package/dist/analyzers/testCheck.js +52 -0
- package/dist/analyzers/testCheck.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +558 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/core/dependencyAnalyzer.d.ts +2 -0
- package/dist/core/dependencyAnalyzer.js +96 -0
- package/dist/core/dependencyAnalyzer.js.map +1 -0
- package/dist/core/frameworkDetector.d.ts +2 -0
- package/dist/core/frameworkDetector.js +152 -0
- package/dist/core/frameworkDetector.js.map +1 -0
- package/dist/core/issueEngine.d.ts +2 -0
- package/dist/core/issueEngine.js +21 -0
- package/dist/core/issueEngine.js.map +1 -0
- package/dist/core/languageDetector.d.ts +2 -0
- package/dist/core/languageDetector.js +107 -0
- package/dist/core/languageDetector.js.map +1 -0
- package/dist/core/repositoryScanner.d.ts +2 -0
- package/dist/core/repositoryScanner.js +79 -0
- package/dist/core/repositoryScanner.js.map +1 -0
- package/dist/fixes/editorconfigFix.d.ts +2 -0
- package/dist/fixes/editorconfigFix.js +28 -0
- package/dist/fixes/editorconfigFix.js.map +1 -0
- package/dist/fixes/eslintFix.d.ts +2 -0
- package/dist/fixes/eslintFix.js +46 -0
- package/dist/fixes/eslintFix.js.map +1 -0
- package/dist/fixes/fixRegistry.d.ts +3 -0
- package/dist/fixes/fixRegistry.js +24 -0
- package/dist/fixes/fixRegistry.js.map +1 -0
- package/dist/fixes/prettierFix.d.ts +2 -0
- package/dist/fixes/prettierFix.js +39 -0
- package/dist/fixes/prettierFix.js.map +1 -0
- package/dist/fixes/testFix.d.ts +2 -0
- package/dist/fixes/testFix.js +61 -0
- package/dist/fixes/testFix.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/reporters/consoleReporter.d.ts +9 -0
- package/dist/reporters/consoleReporter.js +240 -0
- package/dist/reporters/consoleReporter.js.map +1 -0
- package/dist/reporters/jsonReporter.d.ts +7 -0
- package/dist/reporters/jsonReporter.js +27 -0
- package/dist/reporters/jsonReporter.js.map +1 -0
- package/dist/reporters/markdownReporter.d.ts +7 -0
- package/dist/reporters/markdownReporter.js +129 -0
- package/dist/reporters/markdownReporter.js.map +1 -0
- package/dist/types.d.ts +110 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/cache.d.ts +3 -0
- package/dist/utils/cache.js +51 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/fileWalker.d.ts +7 -0
- package/dist/utils/fileWalker.js +45 -0
- package/dist/utils/fileWalker.js.map +1 -0
- package/dist/utils/logger.d.ts +9 -0
- package/dist/utils/logger.js +41 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +48 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Abhi Yoheswaran
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# projscan
|
|
2
|
+
|
|
3
|
+
Instant codebase insights — doctor, x-ray, and architecture map for any repository.
|
|
4
|
+
|
|
5
|
+
Run once, learn something immediately. Run daily, improve your codebase.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g projscan
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or run directly:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx projscan
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
Run inside any repository:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
projscan
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
This runs the default `analyze` command and outputs a full project report including language breakdown, frameworks, structure, issues, and suggestions.
|
|
28
|
+
|
|
29
|
+
For a comprehensive walkthrough of every feature, use case, and output format, see the **[Full Guide](docs/GUIDE.md)**.
|
|
30
|
+
|
|
31
|
+
## Commands
|
|
32
|
+
|
|
33
|
+
### `projscan analyze`
|
|
34
|
+
|
|
35
|
+
Full project analysis — language detection, framework detection, dependency audit, and issue scanning.
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
projscan analyze
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### `projscan doctor`
|
|
42
|
+
|
|
43
|
+
Health check for your project. Detects missing tooling, architecture smells, and dependency risks.
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
projscan doctor
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Example output:
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
Project Health Report
|
|
53
|
+
──────────────────────────────────────────
|
|
54
|
+
|
|
55
|
+
Found 3 warnings, 2 info
|
|
56
|
+
|
|
57
|
+
Issues Detected
|
|
58
|
+
──────────────────────────────────────────
|
|
59
|
+
⚠ No ESLint configuration
|
|
60
|
+
⚠ No Prettier configuration
|
|
61
|
+
⚠ No test framework detected
|
|
62
|
+
ℹ Missing .editorconfig
|
|
63
|
+
ℹ README is nearly empty
|
|
64
|
+
|
|
65
|
+
Recommendations
|
|
66
|
+
──────────────────────────────────────────
|
|
67
|
+
1. Fix: No ESLint configuration
|
|
68
|
+
2. Fix: No Prettier configuration
|
|
69
|
+
3. Fix: No test framework detected
|
|
70
|
+
4. Fix: Missing .editorconfig
|
|
71
|
+
|
|
72
|
+
Run projscan fix to auto-fix 4 issues.
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### `projscan fix`
|
|
76
|
+
|
|
77
|
+
Automatically installs and configures missing developer tools.
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
projscan fix
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Detects issues, proposes fixes, prompts for confirmation, then applies:
|
|
84
|
+
|
|
85
|
+
- Installs ESLint with TypeScript support
|
|
86
|
+
- Installs Prettier with sensible defaults
|
|
87
|
+
- Installs Vitest with a sample test
|
|
88
|
+
- Creates `.editorconfig`
|
|
89
|
+
|
|
90
|
+
Use `-y` to skip the prompt:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
projscan fix -y
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### `projscan explain <file>`
|
|
97
|
+
|
|
98
|
+
Explains a file — its purpose, imports, exports, and potential issues.
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
projscan explain src/services/payment.ts
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### `projscan diagram`
|
|
105
|
+
|
|
106
|
+
Generates an ASCII architecture diagram showing project layers and technologies.
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
projscan diagram
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### `projscan structure`
|
|
113
|
+
|
|
114
|
+
Shows the project directory tree with file counts.
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
projscan structure
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### `projscan dependencies`
|
|
121
|
+
|
|
122
|
+
Analyzes project dependencies — counts, risks, and recommendations.
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
projscan dependencies
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Output Formats
|
|
129
|
+
|
|
130
|
+
All commands support `--format` for different output targets:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
projscan analyze --format json
|
|
134
|
+
projscan doctor --format markdown > HEALTH.md
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Formats: `console` (default), `json`, `markdown`
|
|
138
|
+
|
|
139
|
+
## Options
|
|
140
|
+
|
|
141
|
+
| Flag | Description |
|
|
142
|
+
|------|-------------|
|
|
143
|
+
| `--format <type>` | Output format: console, json, markdown |
|
|
144
|
+
| `--verbose` | Enable debug output |
|
|
145
|
+
| `--quiet` | Suppress non-essential output |
|
|
146
|
+
| `-V, --version` | Show version |
|
|
147
|
+
| `-h, --help` | Show help |
|
|
148
|
+
|
|
149
|
+
## Performance
|
|
150
|
+
|
|
151
|
+
ProjScan is designed for speed:
|
|
152
|
+
|
|
153
|
+
- 5,000 files analyzed in under 1.5 seconds
|
|
154
|
+
- 20,000 files analyzed in under 3 seconds
|
|
155
|
+
- Zero network requests — everything runs locally
|
|
156
|
+
- Minimal dependencies (4 runtime packages)
|
|
157
|
+
|
|
158
|
+
## What It Detects
|
|
159
|
+
|
|
160
|
+
**Languages**: TypeScript, JavaScript, Python, Go, Rust, Java, Ruby, C/C++, PHP, Swift, Kotlin, and 20+ more
|
|
161
|
+
|
|
162
|
+
**Frameworks**: React, Next.js, Vue, Nuxt, Svelte, Angular, Express, Fastify, NestJS, Vite, Tailwind CSS, Prisma, and more
|
|
163
|
+
|
|
164
|
+
**Issues**:
|
|
165
|
+
- Missing ESLint configuration
|
|
166
|
+
- Missing Prettier configuration
|
|
167
|
+
- Missing test framework
|
|
168
|
+
- Missing `.editorconfig`
|
|
169
|
+
- Large utility directories (architecture smell)
|
|
170
|
+
- Excessive dependencies
|
|
171
|
+
- Deprecated packages
|
|
172
|
+
- Wildcard version ranges
|
|
173
|
+
- Missing lockfile
|
|
174
|
+
|
|
175
|
+
## Development
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
git clone https://github.com/your-org/projscan.git
|
|
179
|
+
cd projscan
|
|
180
|
+
npm install
|
|
181
|
+
npm run build
|
|
182
|
+
npm link
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Run tests:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
npm test
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## License
|
|
192
|
+
|
|
193
|
+
MIT
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
const LARGE_DIR_THRESHOLD = 10;
|
|
3
|
+
const SUSPECT_DIRS = ['utils', 'helpers', 'lib', 'shared'];
|
|
4
|
+
export async function check(rootPath, files) {
|
|
5
|
+
const issues = [];
|
|
6
|
+
// Check for large utility directories
|
|
7
|
+
const dirFileCounts = new Map();
|
|
8
|
+
for (const file of files) {
|
|
9
|
+
const parts = file.directory.split(path.sep);
|
|
10
|
+
for (const part of parts) {
|
|
11
|
+
if (SUSPECT_DIRS.includes(part)) {
|
|
12
|
+
const key = file.directory;
|
|
13
|
+
dirFileCounts.set(key, (dirFileCounts.get(key) ?? 0) + 1);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
for (const [dir, count] of dirFileCounts) {
|
|
18
|
+
if (count > LARGE_DIR_THRESHOLD) {
|
|
19
|
+
const dirName = path.basename(dir);
|
|
20
|
+
issues.push({
|
|
21
|
+
id: `large-${dirName}-dir`,
|
|
22
|
+
title: `Large ${dirName}/ directory (${count} files)`,
|
|
23
|
+
description: `The ${dir}/ directory contains ${count} files. Consider splitting into domain-specific modules for better organization.`,
|
|
24
|
+
severity: 'warning',
|
|
25
|
+
category: 'architecture',
|
|
26
|
+
fixAvailable: false,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// Check for missing source directory organization
|
|
31
|
+
const hasSourceDir = files.some((f) => {
|
|
32
|
+
const topDir = f.directory.split(path.sep)[0];
|
|
33
|
+
return ['src', 'lib', 'app', 'pages', 'source'].includes(topDir);
|
|
34
|
+
});
|
|
35
|
+
const hasCodeFiles = files.some((f) => ['.js', '.jsx', '.ts', '.tsx', '.py', '.go', '.rs', '.java'].includes(f.extension));
|
|
36
|
+
if (hasCodeFiles && !hasSourceDir) {
|
|
37
|
+
const rootCodeFiles = files.filter((f) => (!f.directory || f.directory === '.') &&
|
|
38
|
+
['.js', '.jsx', '.ts', '.tsx'].includes(f.extension));
|
|
39
|
+
if (rootCodeFiles.length > 3) {
|
|
40
|
+
issues.push({
|
|
41
|
+
id: 'no-source-dir',
|
|
42
|
+
title: 'No source directory organization',
|
|
43
|
+
description: 'Source code files are at the project root without a src/ or app/ directory. Consider organizing code into a source directory.',
|
|
44
|
+
severity: 'info',
|
|
45
|
+
category: 'architecture',
|
|
46
|
+
fixAvailable: false,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// Check for missing .editorconfig
|
|
51
|
+
const hasEditorConfig = files.some((f) => path.basename(f.relativePath) === '.editorconfig' && (!f.directory || f.directory === '.'));
|
|
52
|
+
if (!hasEditorConfig) {
|
|
53
|
+
issues.push({
|
|
54
|
+
id: 'missing-editorconfig',
|
|
55
|
+
title: 'Missing .editorconfig',
|
|
56
|
+
description: 'No .editorconfig file found. EditorConfig helps maintain consistent coding styles across different editors.',
|
|
57
|
+
severity: 'info',
|
|
58
|
+
category: 'architecture',
|
|
59
|
+
fixAvailable: true,
|
|
60
|
+
fixId: 'add-editorconfig',
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
// Check for missing or empty README
|
|
64
|
+
const readmeFile = files.find((f) => {
|
|
65
|
+
const name = path.basename(f.relativePath).toLowerCase();
|
|
66
|
+
return ((name === 'readme.md' || name === 'readme' || name === 'readme.txt') &&
|
|
67
|
+
(!f.directory || f.directory === '.'));
|
|
68
|
+
});
|
|
69
|
+
if (!readmeFile) {
|
|
70
|
+
issues.push({
|
|
71
|
+
id: 'missing-readme',
|
|
72
|
+
title: 'Missing README',
|
|
73
|
+
description: 'No README file found. A README is essential for project documentation and onboarding.',
|
|
74
|
+
severity: 'warning',
|
|
75
|
+
category: 'architecture',
|
|
76
|
+
fixAvailable: false,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
else if (readmeFile.sizeBytes < 50) {
|
|
80
|
+
issues.push({
|
|
81
|
+
id: 'empty-readme',
|
|
82
|
+
title: 'README is nearly empty',
|
|
83
|
+
description: 'The README file contains very little content. Consider adding project description, setup instructions, and usage examples.',
|
|
84
|
+
severity: 'info',
|
|
85
|
+
category: 'architecture',
|
|
86
|
+
fixAvailable: false,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
return issues;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=architectureCheck.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"architectureCheck.js","sourceRoot":"","sources":["../../src/analyzers/architectureCheck.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { analyzeDependencies } from '../core/dependencyAnalyzer.js';
|
|
2
|
+
export async function check(rootPath, _files) {
|
|
3
|
+
const report = await analyzeDependencies(rootPath);
|
|
4
|
+
if (!report)
|
|
5
|
+
return [];
|
|
6
|
+
return report.risks.map((risk) => ({
|
|
7
|
+
id: `dep-risk-${risk.name}`,
|
|
8
|
+
title: risk.name === 'excessive-dependencies' || risk.name === 'many-dependencies' || risk.name === 'no-lockfile'
|
|
9
|
+
? risk.reason
|
|
10
|
+
: `Dependency risk: ${risk.name}`,
|
|
11
|
+
description: risk.reason,
|
|
12
|
+
severity: risk.severity === 'high' ? 'error' : risk.severity === 'medium' ? 'warning' : 'info',
|
|
13
|
+
category: 'dependencies',
|
|
14
|
+
fixAvailable: false,
|
|
15
|
+
}));
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=dependencyRiskCheck.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dependencyRiskCheck.js","sourceRoot":"","sources":["../../src/analyzers/dependencyRiskCheck.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAGpE,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,QAAgB,EAAE,MAAmB;IAC/D,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IACnD,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IAEvB,OAAO,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACjC,EAAE,EAAE,YAAY,IAAI,CAAC,IAAI,EAAE;QAC3B,KAAK,EAAE,IAAI,CAAC,IAAI,KAAK,wBAAwB,IAAI,IAAI,CAAC,IAAI,KAAK,mBAAmB,IAAI,IAAI,CAAC,IAAI,KAAK,aAAa;YAC/G,CAAC,CAAC,IAAI,CAAC,MAAM;YACb,CAAC,CAAC,oBAAoB,IAAI,CAAC,IAAI,EAAE;QACnC,WAAW,EAAE,IAAI,CAAC,MAAM;QACxB,QAAQ,EAAE,IAAI,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC,OAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAkB,CAAC,CAAC,CAAC,MAAe;QACzH,QAAQ,EAAE,cAAc;QACxB,YAAY,EAAE,KAAK;KACpB,CAAC,CAAC,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
const ESLINT_CONFIG_FILES = [
|
|
4
|
+
'.eslintrc',
|
|
5
|
+
'.eslintrc.js',
|
|
6
|
+
'.eslintrc.cjs',
|
|
7
|
+
'.eslintrc.json',
|
|
8
|
+
'.eslintrc.yml',
|
|
9
|
+
'.eslintrc.yaml',
|
|
10
|
+
'eslint.config.js',
|
|
11
|
+
'eslint.config.mjs',
|
|
12
|
+
'eslint.config.cjs',
|
|
13
|
+
'eslint.config.ts',
|
|
14
|
+
'eslint.config.mts',
|
|
15
|
+
];
|
|
16
|
+
export async function check(rootPath, files) {
|
|
17
|
+
const rootFiles = new Set(files.filter((f) => !f.directory || f.directory === '.').map((f) => path.basename(f.relativePath)));
|
|
18
|
+
// Check for config files
|
|
19
|
+
for (const configFile of ESLINT_CONFIG_FILES) {
|
|
20
|
+
if (rootFiles.has(configFile))
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
// Check for eslintConfig in package.json
|
|
24
|
+
try {
|
|
25
|
+
const raw = await fs.readFile(path.join(rootPath, 'package.json'), 'utf-8');
|
|
26
|
+
const pkg = JSON.parse(raw);
|
|
27
|
+
if (pkg.eslintConfig)
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// No package.json or invalid JSON
|
|
32
|
+
}
|
|
33
|
+
// Check if this is a JS/TS project (ESLint only relevant for these)
|
|
34
|
+
const hasJsTs = files.some((f) => ['.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs'].includes(f.extension));
|
|
35
|
+
if (!hasJsTs)
|
|
36
|
+
return [];
|
|
37
|
+
return [
|
|
38
|
+
{
|
|
39
|
+
id: 'missing-eslint',
|
|
40
|
+
title: 'No ESLint configuration',
|
|
41
|
+
description: 'No ESLint configuration file detected. ESLint helps catch bugs and enforce code style.',
|
|
42
|
+
severity: 'warning',
|
|
43
|
+
category: 'linting',
|
|
44
|
+
fixAvailable: true,
|
|
45
|
+
fixId: 'add-eslint',
|
|
46
|
+
},
|
|
47
|
+
];
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=eslintCheck.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eslintCheck.js","sourceRoot":"","sources":["../../src/analyzers/eslintCheck.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,MAAM,mBAAmB,GAAG;IAC1B,WAAW;IACX,cAAc;IACd,eAAe;IACf,gBAAgB;IAChB,eAAe;IACf,gBAAgB;IAChB,kBAAkB;IAClB,mBAAmB;IACnB,mBAAmB;IACnB,kBAAkB;IAClB,mBAAmB;CACpB,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,QAAgB,EAAE,KAAkB;IAC9D,MAAM,SAAS,GAAG,IAAI,GAAG,CACvB,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,SAAS,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CACnG,CAAC;IAEF,yBAAyB;IACzB,KAAK,MAAM,UAAU,IAAI,mBAAmB,EAAE,CAAC;QAC7C,IAAI,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC;YAAE,OAAO,EAAE,CAAC;IAC3C,CAAC;IAED,yCAAyC;IACzC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,CAAC;QAC5E,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,GAAG,CAAC,YAAY;YAAE,OAAO,EAAE,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,kCAAkC;IACpC,CAAC;IAED,oEAAoE;IACpE,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAC/B,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CACrE,CAAC;IAEF,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IAExB,OAAO;QACL;YACE,EAAE,EAAE,gBAAgB;YACpB,KAAK,EAAE,yBAAyB;YAChC,WAAW,EACT,wFAAwF;YAC1F,QAAQ,EAAE,SAAS;YACnB,QAAQ,EAAE,SAAS;YACnB,YAAY,EAAE,IAAI;YAClB,KAAK,EAAE,YAAY;SACpB;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
const PRETTIER_CONFIG_FILES = [
|
|
4
|
+
'.prettierrc',
|
|
5
|
+
'.prettierrc.js',
|
|
6
|
+
'.prettierrc.cjs',
|
|
7
|
+
'.prettierrc.mjs',
|
|
8
|
+
'.prettierrc.json',
|
|
9
|
+
'.prettierrc.yml',
|
|
10
|
+
'.prettierrc.yaml',
|
|
11
|
+
'.prettierrc.toml',
|
|
12
|
+
'prettier.config.js',
|
|
13
|
+
'prettier.config.cjs',
|
|
14
|
+
'prettier.config.mjs',
|
|
15
|
+
'prettier.config.ts',
|
|
16
|
+
];
|
|
17
|
+
export async function check(rootPath, files) {
|
|
18
|
+
const rootFiles = new Set(files.filter((f) => !f.directory || f.directory === '.').map((f) => path.basename(f.relativePath)));
|
|
19
|
+
for (const configFile of PRETTIER_CONFIG_FILES) {
|
|
20
|
+
if (rootFiles.has(configFile))
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
// Check for prettier key in package.json
|
|
24
|
+
try {
|
|
25
|
+
const raw = await fs.readFile(path.join(rootPath, 'package.json'), 'utf-8');
|
|
26
|
+
const pkg = JSON.parse(raw);
|
|
27
|
+
if (pkg.prettier)
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// No package.json
|
|
32
|
+
}
|
|
33
|
+
// Only relevant for JS/TS/CSS/HTML projects
|
|
34
|
+
const relevantExtensions = new Set([
|
|
35
|
+
'.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs',
|
|
36
|
+
'.css', '.scss', '.less', '.html', '.vue', '.svelte', '.json',
|
|
37
|
+
]);
|
|
38
|
+
const hasRelevantFiles = files.some((f) => relevantExtensions.has(f.extension));
|
|
39
|
+
if (!hasRelevantFiles)
|
|
40
|
+
return [];
|
|
41
|
+
return [
|
|
42
|
+
{
|
|
43
|
+
id: 'missing-prettier',
|
|
44
|
+
title: 'No Prettier configuration',
|
|
45
|
+
description: 'No Prettier configuration file detected. Prettier ensures consistent code formatting across your project.',
|
|
46
|
+
severity: 'warning',
|
|
47
|
+
category: 'formatting',
|
|
48
|
+
fixAvailable: true,
|
|
49
|
+
fixId: 'add-prettier',
|
|
50
|
+
},
|
|
51
|
+
];
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=prettierCheck.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prettierCheck.js","sourceRoot":"","sources":["../../src/analyzers/prettierCheck.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,MAAM,qBAAqB,GAAG;IAC5B,aAAa;IACb,gBAAgB;IAChB,iBAAiB;IACjB,iBAAiB;IACjB,kBAAkB;IAClB,iBAAiB;IACjB,kBAAkB;IAClB,kBAAkB;IAClB,oBAAoB;IACpB,qBAAqB;IACrB,qBAAqB;IACrB,oBAAoB;CACrB,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,QAAgB,EAAE,KAAkB;IAC9D,MAAM,SAAS,GAAG,IAAI,GAAG,CACvB,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,SAAS,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CACnG,CAAC;IAEF,KAAK,MAAM,UAAU,IAAI,qBAAqB,EAAE,CAAC;QAC/C,IAAI,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC;YAAE,OAAO,EAAE,CAAC;IAC3C,CAAC;IAED,yCAAyC;IACzC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,CAAC;QAC5E,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,GAAG,CAAC,QAAQ;YAAE,OAAO,EAAE,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,kBAAkB;IACpB,CAAC;IAED,4CAA4C;IAC5C,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC;QACjC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;QAC5C,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO;KAC9D,CAAC,CAAC;IACH,MAAM,gBAAgB,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;IAChF,IAAI,CAAC,gBAAgB;QAAE,OAAO,EAAE,CAAC;IAEjC,OAAO;QACL;YACE,EAAE,EAAE,kBAAkB;YACtB,KAAK,EAAE,2BAA2B;YAClC,WAAW,EACT,2GAA2G;YAC7G,QAAQ,EAAE,SAAS;YACnB,QAAQ,EAAE,YAAY;YACtB,YAAY,EAAE,IAAI;YAClB,KAAK,EAAE,cAAc;SACtB;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
const TEST_FRAMEWORKS = ['vitest', 'jest', 'mocha', 'ava', 'tap', 'jasmine', '@playwright/test', 'cypress'];
|
|
4
|
+
const TEST_FILE_PATTERNS = ['.test.', '.spec.', '__tests__'];
|
|
5
|
+
export async function check(rootPath, files) {
|
|
6
|
+
const issues = [];
|
|
7
|
+
// Check if this is a JS/TS project
|
|
8
|
+
const hasJsTs = files.some((f) => ['.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs'].includes(f.extension));
|
|
9
|
+
if (!hasJsTs)
|
|
10
|
+
return [];
|
|
11
|
+
// Check for test framework in dependencies
|
|
12
|
+
let hasTestFramework = false;
|
|
13
|
+
try {
|
|
14
|
+
const raw = await fs.readFile(path.join(rootPath, 'package.json'), 'utf-8');
|
|
15
|
+
const pkg = JSON.parse(raw);
|
|
16
|
+
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
17
|
+
for (const framework of TEST_FRAMEWORKS) {
|
|
18
|
+
if (allDeps[framework]) {
|
|
19
|
+
hasTestFramework = true;
|
|
20
|
+
break;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// No package.json
|
|
26
|
+
}
|
|
27
|
+
// Check for test files
|
|
28
|
+
const testFiles = files.filter((f) => TEST_FILE_PATTERNS.some((pattern) => f.relativePath.includes(pattern)));
|
|
29
|
+
if (!hasTestFramework) {
|
|
30
|
+
issues.push({
|
|
31
|
+
id: 'missing-test-framework',
|
|
32
|
+
title: 'No test framework detected',
|
|
33
|
+
description: 'No testing framework found in dependencies. Testing is essential for code quality and reliability.',
|
|
34
|
+
severity: 'warning',
|
|
35
|
+
category: 'testing',
|
|
36
|
+
fixAvailable: true,
|
|
37
|
+
fixId: 'add-tests',
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
else if (testFiles.length === 0) {
|
|
41
|
+
issues.push({
|
|
42
|
+
id: 'no-test-files',
|
|
43
|
+
title: 'No test files found',
|
|
44
|
+
description: 'A test framework is configured but no test files were found. Consider adding tests for your code.',
|
|
45
|
+
severity: 'info',
|
|
46
|
+
category: 'testing',
|
|
47
|
+
fixAvailable: false,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return issues;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=testCheck.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testCheck.js","sourceRoot":"","sources":["../../src/analyzers/testCheck.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,MAAM,eAAe,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,kBAAkB,EAAE,SAAS,CAAC,CAAC;AAE5G,MAAM,kBAAkB,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC;AAE7D,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,QAAgB,EAAE,KAAkB;IAC9D,MAAM,MAAM,GAAY,EAAE,CAAC;IAE3B,mCAAmC;IACnC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAC/B,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CACrE,CAAC;IACF,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IAExB,2CAA2C;IAC3C,IAAI,gBAAgB,GAAG,KAAK,CAAC;IAC7B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,CAAC;QAC5E,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC5B,MAAM,OAAO,GAAG,EAAE,GAAG,GAAG,CAAC,YAAY,EAAE,GAAG,GAAG,CAAC,eAAe,EAAE,CAAC;QAEhE,KAAK,MAAM,SAAS,IAAI,eAAe,EAAE,CAAC;YACxC,IAAI,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;gBACvB,gBAAgB,GAAG,IAAI,CAAC;gBACxB,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,kBAAkB;IACpB,CAAC;IAED,uBAAuB;IACvB,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CACnC,kBAAkB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CACvE,CAAC;IAEF,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACtB,MAAM,CAAC,IAAI,CAAC;YACV,EAAE,EAAE,wBAAwB;YAC5B,KAAK,EAAE,4BAA4B;YACnC,WAAW,EACT,oGAAoG;YACtG,QAAQ,EAAE,SAAS;YACnB,QAAQ,EAAE,SAAS;YACnB,YAAY,EAAE,IAAI;YAClB,KAAK,EAAE,WAAW;SACnB,CAAC,CAAC;IACL,CAAC;SAAM,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClC,MAAM,CAAC,IAAI,CAAC;YACV,EAAE,EAAE,eAAe;YACnB,KAAK,EAAE,qBAAqB;YAC5B,WAAW,EACT,mGAAmG;YACrG,QAAQ,EAAE,MAAM;YAChB,QAAQ,EAAE,SAAS;YACnB,YAAY,EAAE,KAAK;SACpB,CAAC,CAAC;IACL,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
|