@sun-asterisk/sunlint 1.3.9 → 1.3.11
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/CHANGELOG.md +91 -1
- package/README.md +62 -0
- package/config/rules/enhanced-rules-registry.json +18 -0
- package/core/cli-action-handler.js +30 -1
- package/core/cli-program.js +5 -1
- package/core/output-service.js +160 -3
- package/core/scoring-service.js +169 -0
- package/core/semantic-engine.js +4 -2
- package/core/summary-report-service.js +189 -0
- package/core/upload-service.js +282 -0
- package/docs/QUALITY_SCORING_GUIDE.md +397 -0
- package/package.json +1 -1
- package/rules/common/C024_no_scatter_hardcoded_constants/symbol-based-analyzer.js +116 -10
- package/rules/common/C060_no_override_superclass/analyzer.js +180 -0
- package/rules/common/C060_no_override_superclass/config.json +50 -0
- package/rules/common/C060_no_override_superclass/symbol-based-analyzer.js +220 -0
- package/rules/index.js +1 -0
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
# Quality Scoring & Summary Report Guide
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
SunLint includes a comprehensive quality scoring system and summary report generation feature designed for CI/CD integration and management dashboards.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
### 1. Quality Scoring System
|
|
10
|
+
|
|
11
|
+
The quality score is calculated based on multiple factors:
|
|
12
|
+
|
|
13
|
+
```javascript
|
|
14
|
+
Score Formula: 100 - (errorCount × 5 + warningCount × 1) × (1000 / LOC) + (rulesChecked × 0.5)
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Scoring Components:**
|
|
18
|
+
- **Base Score**: Starts at 100 (perfect quality)
|
|
19
|
+
- **Error Penalty**: -5 points per error
|
|
20
|
+
- **Warning Penalty**: -1 point per warning
|
|
21
|
+
- **LOC Normalization**: Violations are normalized per 1000 lines of code
|
|
22
|
+
- **Rule Bonus**: +0.5 points per rule checked (max 10 points)
|
|
23
|
+
- **Final Range**: 0-100
|
|
24
|
+
|
|
25
|
+
**Grade Scale:**
|
|
26
|
+
- **A+**: 95-100
|
|
27
|
+
- **A**: 90-94
|
|
28
|
+
- **B+**: 85-89
|
|
29
|
+
- **B**: 80-84
|
|
30
|
+
- **C+**: 75-79
|
|
31
|
+
- **C**: 70-74
|
|
32
|
+
- **D**: 60-69
|
|
33
|
+
- **F**: Below 60
|
|
34
|
+
|
|
35
|
+
### 2. Summary Report Format
|
|
36
|
+
|
|
37
|
+
The summary report is generated in JSON format, designed for easy integration with CI/CD pipelines and management dashboards.
|
|
38
|
+
|
|
39
|
+
**Example Output:**
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"metadata": {
|
|
43
|
+
"generated_at": "2025-10-07T03:48:00.636Z",
|
|
44
|
+
"tool": "SunLint",
|
|
45
|
+
"version": "1.3.9",
|
|
46
|
+
"analysis_duration_ms": 390
|
|
47
|
+
},
|
|
48
|
+
"repository": {
|
|
49
|
+
"repository_url": "https://github.com/org/repo",
|
|
50
|
+
"branch": "main",
|
|
51
|
+
"commit_hash": "abc123"
|
|
52
|
+
},
|
|
53
|
+
"quality": {
|
|
54
|
+
"score": 98.9,
|
|
55
|
+
"grade": "A+",
|
|
56
|
+
"metrics": {
|
|
57
|
+
"errors": 0,
|
|
58
|
+
"warnings": 520,
|
|
59
|
+
"rulesChecked": 1,
|
|
60
|
+
"linesOfCode": 319709,
|
|
61
|
+
"violationsPerKLOC": 1.6
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
"violations": {
|
|
65
|
+
"total": 520,
|
|
66
|
+
"by_severity": {
|
|
67
|
+
"errors": 0,
|
|
68
|
+
"warnings": 520
|
|
69
|
+
},
|
|
70
|
+
"by_rule": [
|
|
71
|
+
{
|
|
72
|
+
"rule_code": "C065",
|
|
73
|
+
"count": 520,
|
|
74
|
+
"severity": "warning"
|
|
75
|
+
}
|
|
76
|
+
]
|
|
77
|
+
},
|
|
78
|
+
"analysis": {
|
|
79
|
+
"files_analyzed": 1000,
|
|
80
|
+
"rules_checked": 1,
|
|
81
|
+
"lines_of_code": 319709
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Usage
|
|
87
|
+
|
|
88
|
+
### Basic Usage
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
# Generate summary report only
|
|
92
|
+
node cli.js --input=src --rule=C065 --output-summary=summary.json
|
|
93
|
+
|
|
94
|
+
# Generate both detailed report and summary report
|
|
95
|
+
node cli.js --input=src --rule=C065 --output=report.txt --output-summary=summary.json
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### CI/CD Integration
|
|
99
|
+
|
|
100
|
+
#### GitHub Actions Example
|
|
101
|
+
|
|
102
|
+
```yaml
|
|
103
|
+
name: Code Quality Check
|
|
104
|
+
|
|
105
|
+
on:
|
|
106
|
+
pull_request:
|
|
107
|
+
branches: [ main ]
|
|
108
|
+
push:
|
|
109
|
+
branches: [ main ]
|
|
110
|
+
|
|
111
|
+
jobs:
|
|
112
|
+
quality-check:
|
|
113
|
+
runs-on: ubuntu-latest
|
|
114
|
+
|
|
115
|
+
steps:
|
|
116
|
+
- uses: actions/checkout@v3
|
|
117
|
+
|
|
118
|
+
- name: Setup Node.js
|
|
119
|
+
uses: actions/setup-node@v3
|
|
120
|
+
with:
|
|
121
|
+
node-version: '18'
|
|
122
|
+
|
|
123
|
+
- name: Install dependencies
|
|
124
|
+
run: npm install
|
|
125
|
+
|
|
126
|
+
- name: Run SunLint Analysis
|
|
127
|
+
run: |
|
|
128
|
+
node cli.js \
|
|
129
|
+
--input=src \
|
|
130
|
+
--rule=C001,C005,C015,C065 \
|
|
131
|
+
--output-summary=quality-report.json
|
|
132
|
+
env:
|
|
133
|
+
GITHUB_REPOSITORY: ${{ github.repository }}
|
|
134
|
+
GITHUB_REF_NAME: ${{ github.ref_name }}
|
|
135
|
+
GITHUB_SHA: ${{ github.sha }}
|
|
136
|
+
|
|
137
|
+
- name: Upload Quality Report
|
|
138
|
+
uses: actions/upload-artifact@v3
|
|
139
|
+
with:
|
|
140
|
+
name: quality-report
|
|
141
|
+
path: quality-report.json
|
|
142
|
+
|
|
143
|
+
- name: Post to Dashboard
|
|
144
|
+
run: |
|
|
145
|
+
curl -X POST https://your-dashboard.com/api/quality \
|
|
146
|
+
-H "Content-Type: application/json" \
|
|
147
|
+
-H "Authorization: Bearer ${{ secrets.DASHBOARD_TOKEN }}" \
|
|
148
|
+
-d @quality-report.json
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Environment Variables
|
|
152
|
+
|
|
153
|
+
The summary report automatically detects and uses Git information from environment variables (commonly available in CI/CD):
|
|
154
|
+
|
|
155
|
+
| Variable | Description | Source |
|
|
156
|
+
|----------|-------------|--------|
|
|
157
|
+
| `GITHUB_REPOSITORY` | Repository name (e.g., `owner/repo`) | GitHub Actions |
|
|
158
|
+
| `GITHUB_REF_NAME` | Branch or tag name | GitHub Actions |
|
|
159
|
+
| `GITHUB_SHA` | Commit hash | GitHub Actions |
|
|
160
|
+
|
|
161
|
+
If these variables are not available, the tool will automatically detect Git information from the local repository.
|
|
162
|
+
|
|
163
|
+
## CLI Options
|
|
164
|
+
|
|
165
|
+
### `--output-summary <file>`
|
|
166
|
+
|
|
167
|
+
Generate a summary report in JSON format suitable for CI/CD and management dashboards.
|
|
168
|
+
|
|
169
|
+
**Differences from `--output`:**
|
|
170
|
+
|
|
171
|
+
| Feature | `--output` | `--output-summary` |
|
|
172
|
+
|---------|-----------|-------------------|
|
|
173
|
+
| Format | Text/JSON (ESLint-compatible) | JSON (Custom format) |
|
|
174
|
+
| Detail Level | Full violation details | Summary only |
|
|
175
|
+
| File Size | Large (includes all details) | Small (summary only) |
|
|
176
|
+
| Purpose | Detailed debugging | CI/CD integration |
|
|
177
|
+
| Includes Score | ❌ No | ✅ Yes |
|
|
178
|
+
| Includes LOC | ❌ No | ✅ Yes |
|
|
179
|
+
| Git Info | ❌ No | ✅ Yes |
|
|
180
|
+
| Violation Count by Rule | ❌ No | ✅ Yes |
|
|
181
|
+
|
|
182
|
+
**Example:**
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
# Detailed report: Shows every violation with line numbers and messages
|
|
186
|
+
node cli.js --input=src --rule=C065 --output=detailed-report.txt
|
|
187
|
+
|
|
188
|
+
# Summary report: Shows total count per rule, score, and metrics
|
|
189
|
+
node cli.js --input=src --rule=C065 --output-summary=summary.json
|
|
190
|
+
|
|
191
|
+
# Both: Get detailed report for debugging + summary for dashboard
|
|
192
|
+
node cli.js --input=src --rule=C065 \
|
|
193
|
+
--output=detailed-report.txt \
|
|
194
|
+
--output-summary=summary.json
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Integration Examples
|
|
198
|
+
|
|
199
|
+
### 1. Send to Management Dashboard
|
|
200
|
+
|
|
201
|
+
```javascript
|
|
202
|
+
const fs = require('fs');
|
|
203
|
+
const axios = require('axios');
|
|
204
|
+
|
|
205
|
+
const report = JSON.parse(fs.readFileSync('summary.json', 'utf8'));
|
|
206
|
+
|
|
207
|
+
await axios.post('https://dashboard.company.com/api/quality', {
|
|
208
|
+
project: 'my-project',
|
|
209
|
+
...report
|
|
210
|
+
}, {
|
|
211
|
+
headers: {
|
|
212
|
+
'Authorization': `Bearer ${process.env.DASHBOARD_TOKEN}`
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### 2. Slack Notification
|
|
218
|
+
|
|
219
|
+
```javascript
|
|
220
|
+
const fs = require('fs');
|
|
221
|
+
const axios = require('axios');
|
|
222
|
+
|
|
223
|
+
const report = JSON.parse(fs.readFileSync('summary.json', 'utf8'));
|
|
224
|
+
const { quality, violations, analysis } = report;
|
|
225
|
+
|
|
226
|
+
const message = {
|
|
227
|
+
text: `Code Quality Report: ${quality.grade} (${quality.score})`,
|
|
228
|
+
blocks: [
|
|
229
|
+
{
|
|
230
|
+
type: "section",
|
|
231
|
+
text: {
|
|
232
|
+
type: "mrkdwn",
|
|
233
|
+
text: `*Code Quality Analysis*\n` +
|
|
234
|
+
`Score: *${quality.score}* (${quality.grade})\n` +
|
|
235
|
+
`Violations: ${violations.total} (${violations.by_severity.errors} errors, ${violations.by_severity.warnings} warnings)\n` +
|
|
236
|
+
`Files: ${analysis.files_analyzed} | LOC: ${analysis.lines_of_code.toLocaleString()}`
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
]
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
await axios.post(process.env.SLACK_WEBHOOK_URL, message);
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### 3. Quality Gate in CI/CD
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
#!/bin/bash
|
|
249
|
+
|
|
250
|
+
# Run analysis
|
|
251
|
+
node cli.js --input=src --rule=C001,C005,C015,C065 --output-summary=quality.json
|
|
252
|
+
|
|
253
|
+
# Extract score
|
|
254
|
+
SCORE=$(jq -r '.quality.score' quality.json)
|
|
255
|
+
|
|
256
|
+
# Set minimum score threshold
|
|
257
|
+
MIN_SCORE=80
|
|
258
|
+
|
|
259
|
+
# Check if score meets threshold
|
|
260
|
+
if (( $(echo "$SCORE < $MIN_SCORE" | bc -l) )); then
|
|
261
|
+
echo "❌ Quality score $SCORE is below threshold $MIN_SCORE"
|
|
262
|
+
exit 1
|
|
263
|
+
else
|
|
264
|
+
echo "✅ Quality score $SCORE meets threshold"
|
|
265
|
+
exit 0
|
|
266
|
+
fi
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## Best Practices
|
|
270
|
+
|
|
271
|
+
### 1. Rule Selection
|
|
272
|
+
|
|
273
|
+
Choose rules appropriate for your quality goals:
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
# Security focus
|
|
277
|
+
node cli.js --input=src --rule=S001,S004,S010 --output-summary=security.json
|
|
278
|
+
|
|
279
|
+
# Code organization focus
|
|
280
|
+
node cli.js --input=src --rule=C001,C005,C006,C015 --output-summary=organization.json
|
|
281
|
+
|
|
282
|
+
# Testing focus
|
|
283
|
+
node cli.js --input=src --rule=C065,C066 --output-summary=testing.json
|
|
284
|
+
|
|
285
|
+
# Comprehensive check (slower)
|
|
286
|
+
node cli.js --input=src --rule=security,cleancode --output-summary=full.json
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### 2. Performance Optimization
|
|
290
|
+
|
|
291
|
+
For large projects:
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
# Analyze only changed files (PR mode)
|
|
295
|
+
node cli.js --input=src --rule=C001,C005 --changed-files --output-summary=pr-quality.json
|
|
296
|
+
|
|
297
|
+
# Set file limits for faster analysis
|
|
298
|
+
node cli.js --input=src --rule=C001,C005 --max-files=500 --output-summary=quick-check.json
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### 3. Trending Analysis
|
|
302
|
+
|
|
303
|
+
Track quality over time:
|
|
304
|
+
|
|
305
|
+
```javascript
|
|
306
|
+
const fs = require('fs');
|
|
307
|
+
|
|
308
|
+
// Save with timestamp
|
|
309
|
+
const report = JSON.parse(fs.readFileSync('summary.json', 'utf8'));
|
|
310
|
+
const timestamp = new Date().toISOString();
|
|
311
|
+
|
|
312
|
+
// Store in database or time-series storage
|
|
313
|
+
await db.collection('quality_reports').insertOne({
|
|
314
|
+
...report,
|
|
315
|
+
timestamp
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
// Query trends
|
|
319
|
+
const last30Days = await db.collection('quality_reports')
|
|
320
|
+
.find({
|
|
321
|
+
timestamp: { $gte: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) }
|
|
322
|
+
})
|
|
323
|
+
.sort({ timestamp: 1 })
|
|
324
|
+
.toArray();
|
|
325
|
+
|
|
326
|
+
// Calculate trend
|
|
327
|
+
const scores = last30Days.map(r => r.quality.score);
|
|
328
|
+
const trend = scores[scores.length - 1] - scores[0];
|
|
329
|
+
console.log(`Quality trend (30 days): ${trend > 0 ? '+' : ''}${trend.toFixed(1)}`);
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
## Scoring Interpretation
|
|
333
|
+
|
|
334
|
+
### Score Ranges
|
|
335
|
+
|
|
336
|
+
- **95-100 (A+)**: Excellent code quality with minimal violations
|
|
337
|
+
- **90-94 (A)**: Very good quality, minor improvements possible
|
|
338
|
+
- **85-89 (B+)**: Good quality with some areas for improvement
|
|
339
|
+
- **80-84 (B)**: Acceptable quality, several improvements recommended
|
|
340
|
+
- **75-79 (C+)**: Below average, significant improvements needed
|
|
341
|
+
- **70-74 (C)**: Poor quality, requires immediate attention
|
|
342
|
+
- **60-69 (D)**: Very poor quality, major refactoring needed
|
|
343
|
+
- **0-59 (F)**: Critical quality issues
|
|
344
|
+
|
|
345
|
+
### Metrics Understanding
|
|
346
|
+
|
|
347
|
+
**Violations per KLOC (Violations per 1000 Lines of Code)**
|
|
348
|
+
|
|
349
|
+
This metric normalizes violations by code size:
|
|
350
|
+
|
|
351
|
+
- **< 1**: Excellent
|
|
352
|
+
- **1-3**: Good
|
|
353
|
+
- **3-5**: Fair
|
|
354
|
+
- **5-10**: Poor
|
|
355
|
+
- **> 10**: Critical
|
|
356
|
+
|
|
357
|
+
## Troubleshooting
|
|
358
|
+
|
|
359
|
+
### LOC Calculation Issues
|
|
360
|
+
|
|
361
|
+
If LOC is 0 or incorrect:
|
|
362
|
+
|
|
363
|
+
```bash
|
|
364
|
+
# Ensure input path is correct
|
|
365
|
+
node cli.js --input=./src --rule=C065 --output-summary=summary.json
|
|
366
|
+
|
|
367
|
+
# For multiple paths
|
|
368
|
+
node cli.js --input=./src,./lib --rule=C065 --output-summary=summary.json
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### Git Information Not Detected
|
|
372
|
+
|
|
373
|
+
In CI/CD, ensure environment variables are set:
|
|
374
|
+
|
|
375
|
+
```yaml
|
|
376
|
+
env:
|
|
377
|
+
GITHUB_REPOSITORY: ${{ github.repository }}
|
|
378
|
+
GITHUB_REF_NAME: ${{ github.ref_name }}
|
|
379
|
+
GITHUB_SHA: ${{ github.sha }}
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
For local development:
|
|
383
|
+
|
|
384
|
+
```bash
|
|
385
|
+
# Check if git is available
|
|
386
|
+
git rev-parse --git-dir
|
|
387
|
+
|
|
388
|
+
# Run from repository root
|
|
389
|
+
cd /path/to/repo
|
|
390
|
+
node /path/to/sunlint/cli.js --input=src --output-summary=summary.json
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
## See Also
|
|
394
|
+
|
|
395
|
+
- [Configuration Guide](CONFIGURATION.md)
|
|
396
|
+
- [CI/CD Integration Guide](CI-CD-GUIDE.md)
|
|
397
|
+
- [Command Examples](COMMAND-EXAMPLES.md)
|
package/package.json
CHANGED
|
@@ -48,6 +48,14 @@ class C024SymbolBasedAnalyzer {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
try {
|
|
51
|
+
// skip ignored files
|
|
52
|
+
if (this.isIgnoredFile(filePath)) {
|
|
53
|
+
if (verbose) {
|
|
54
|
+
console.log(`🔍 [C024 Symbol-Based] Skipping ignored file: ${filePath}`);
|
|
55
|
+
}
|
|
56
|
+
return violations;
|
|
57
|
+
}
|
|
58
|
+
|
|
51
59
|
const sourceFile = this.semanticEngine.project.getSourceFile(filePath);
|
|
52
60
|
if (!sourceFile) {
|
|
53
61
|
return violations;
|
|
@@ -99,9 +107,7 @@ class C024SymbolBasedAnalyzer {
|
|
|
99
107
|
const kind = node.getKind();
|
|
100
108
|
if (
|
|
101
109
|
kind === SyntaxKind.StringLiteral ||
|
|
102
|
-
kind === SyntaxKind.NumericLiteral
|
|
103
|
-
kind === SyntaxKind.TrueKeyword ||
|
|
104
|
-
kind === SyntaxKind.FalseKeyword
|
|
110
|
+
kind === SyntaxKind.NumericLiteral
|
|
105
111
|
) {
|
|
106
112
|
const text = node.getText().replace(/['"`]/g, ""); // strip quotes
|
|
107
113
|
if (this.isAllowedLiteral(node, text)) return;
|
|
@@ -120,9 +126,27 @@ class C024SymbolBasedAnalyzer {
|
|
|
120
126
|
const kind = node.getKind();
|
|
121
127
|
if (kind === SyntaxKind.VariableDeclaration) {
|
|
122
128
|
const parentKind = node.getParent()?.getKind();
|
|
129
|
+
// Skip detection for `for ... of` loop variable
|
|
130
|
+
const loopAncestor = node.getFirstAncestor((ancestor) => {
|
|
131
|
+
const kind = ancestor.getKind?.();
|
|
132
|
+
return (
|
|
133
|
+
kind === SyntaxKind.ForOfStatement ||
|
|
134
|
+
kind === SyntaxKind.ForInStatement ||
|
|
135
|
+
kind === SyntaxKind.ForStatement ||
|
|
136
|
+
kind === SyntaxKind.WhileStatement ||
|
|
137
|
+
kind === SyntaxKind.DoStatement ||
|
|
138
|
+
kind === SyntaxKind.SwitchStatement
|
|
139
|
+
);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
if (loopAncestor) {
|
|
143
|
+
return; // skip for all loop/switch contexts, no matter how nested
|
|
144
|
+
}
|
|
145
|
+
|
|
123
146
|
if (
|
|
124
147
|
parentKind === SyntaxKind.VariableDeclarationList &&
|
|
125
|
-
node.getParent().getDeclarationKind() === "const"
|
|
148
|
+
node.getParent().getDeclarationKind() === "const" &&
|
|
149
|
+
!node.getInitializer()
|
|
126
150
|
) {
|
|
127
151
|
this.pushViolation(
|
|
128
152
|
violations,
|
|
@@ -154,27 +178,109 @@ class C024SymbolBasedAnalyzer {
|
|
|
154
178
|
|
|
155
179
|
// --- helper: allow safe literals ---
|
|
156
180
|
isAllowedLiteral(node, text) {
|
|
157
|
-
|
|
158
|
-
|
|
181
|
+
const parent = node.getParent();
|
|
182
|
+
|
|
183
|
+
// 1 Skip imports/exports
|
|
184
|
+
if (parent?.getKind() === SyntaxKind.ImportDeclaration) return true;
|
|
185
|
+
if (parent?.getKind() === SyntaxKind.ExportDeclaration) return true;
|
|
186
|
+
|
|
187
|
+
// 2 Skip literals that are inside call expressions (direct or nested)
|
|
188
|
+
if (
|
|
189
|
+
parent?.getKind() === SyntaxKind.CallExpression ||
|
|
190
|
+
parent?.getFirstAncestorByKind(SyntaxKind.CallExpression)
|
|
191
|
+
) {
|
|
159
192
|
return true;
|
|
160
193
|
}
|
|
161
194
|
|
|
162
|
-
|
|
195
|
+
if (
|
|
196
|
+
parent?.getKind() === SyntaxKind.ElementAccessExpression &&
|
|
197
|
+
parent.getArgumentExpression?.() === node
|
|
198
|
+
) {
|
|
199
|
+
return true; // skip array/object key
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// 3 Allow short strings
|
|
163
203
|
if (typeof text === "string" && text.length <= 1) return true;
|
|
164
204
|
|
|
165
|
-
//
|
|
205
|
+
// 4 Allow sentinel numbers
|
|
166
206
|
if (text === "0" || text === "1" || text === "-1") return true;
|
|
167
207
|
|
|
168
|
-
//
|
|
208
|
+
// 5 Allow known safe strings (like "UNKNOWN")
|
|
169
209
|
if (this.safeStrings.includes(text)) return true;
|
|
170
210
|
|
|
211
|
+
// 6 Allow SQL-style placeholders (:variable) inside string/template
|
|
212
|
+
if (typeof text === "string" && /:\w+/.test(text)) {
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
|
|
171
216
|
return false;
|
|
172
217
|
}
|
|
173
218
|
|
|
174
219
|
// helper to check if file is a constants file
|
|
175
220
|
isConstantsFile(filePath) {
|
|
176
221
|
const lower = filePath.toLowerCase();
|
|
177
|
-
|
|
222
|
+
|
|
223
|
+
// common suffixes/patterns for utility or structural files
|
|
224
|
+
const ignoredSuffixes = [
|
|
225
|
+
".constants.ts",
|
|
226
|
+
".const.ts",
|
|
227
|
+
".enum.ts",
|
|
228
|
+
".interface.ts",
|
|
229
|
+
".response.ts",
|
|
230
|
+
".request.ts",
|
|
231
|
+
".res.ts",
|
|
232
|
+
".req.ts",
|
|
233
|
+
];
|
|
234
|
+
|
|
235
|
+
// 1 direct suffix match
|
|
236
|
+
if (ignoredSuffixes.some(suffix => lower.endsWith(suffix))) {
|
|
237
|
+
return true;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// 2 matches dto.xxx.ts (multi-dot dto files)
|
|
241
|
+
if (/\.dto\.[^.]+\.ts$/.test(lower)) {
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// 3 matches folder-based conventions
|
|
246
|
+
if (
|
|
247
|
+
lower.includes("/constants/") ||
|
|
248
|
+
lower.includes("/enums/") ||
|
|
249
|
+
lower.includes("/interfaces/")
|
|
250
|
+
) {
|
|
251
|
+
return true;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
isIgnoredFile(filePath) {
|
|
259
|
+
const ignoredPatterns = [
|
|
260
|
+
/\.test\./i,
|
|
261
|
+
/\.tests\./i,
|
|
262
|
+
/\.spec\./i,
|
|
263
|
+
/\.mock\./i,
|
|
264
|
+
/\.css$/i,
|
|
265
|
+
/\.scss$/i,
|
|
266
|
+
/\.html$/i,
|
|
267
|
+
/\.json$/i,
|
|
268
|
+
/\.md$/i,
|
|
269
|
+
/\.svg$/i,
|
|
270
|
+
/\.png$/i,
|
|
271
|
+
/\.jpg$/i,
|
|
272
|
+
/\.jpeg$/i,
|
|
273
|
+
/\.gif$/i,
|
|
274
|
+
/\.bmp$/i,
|
|
275
|
+
/\.ico$/i,
|
|
276
|
+
/\.lock$/i,
|
|
277
|
+
/\.log$/i,
|
|
278
|
+
/\/test\//i,
|
|
279
|
+
/\/tests\//i,
|
|
280
|
+
/\/spec\//i
|
|
281
|
+
];
|
|
282
|
+
|
|
283
|
+
return ignoredPatterns.some((regex) => regex.test(filePath));
|
|
178
284
|
}
|
|
179
285
|
}
|
|
180
286
|
|