uiaudit.js 1.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.
- package/dist/auditor.d.ts +11 -0
- package/dist/auditor.d.ts.map +1 -0
- package/dist/auditor.js +70 -0
- package/dist/auditor.js.map +1 -0
- package/dist/auditors/accessibility.d.ts +8 -0
- package/dist/auditors/accessibility.d.ts.map +1 -0
- package/dist/auditors/accessibility.js +1769 -0
- package/dist/auditors/accessibility.js.map +1 -0
- package/dist/auditors/performance.d.ts +8 -0
- package/dist/auditors/performance.d.ts.map +1 -0
- package/dist/auditors/performance.js +168 -0
- package/dist/auditors/performance.js.map +1 -0
- package/dist/auditors/seo.d.ts +8 -0
- package/dist/auditors/seo.d.ts.map +1 -0
- package/dist/auditors/seo.js +171 -0
- package/dist/auditors/seo.js.map +1 -0
- package/dist/cli.d.ts +10 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +115 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/parser/index.d.ts +18 -0
- package/dist/parser/index.d.ts.map +1 -0
- package/dist/parser/index.js +87 -0
- package/dist/parser/index.js.map +1 -0
- package/dist/parser/traverse.d.ts +10 -0
- package/dist/parser/traverse.d.ts.map +1 -0
- package/dist/parser/traverse.js +13 -0
- package/dist/parser/traverse.js.map +1 -0
- package/dist/reporter/terminal.d.ts +3 -0
- package/dist/reporter/terminal.d.ts.map +1 -0
- package/dist/reporter/terminal.js +200 -0
- package/dist/reporter/terminal.js.map +1 -0
- package/dist/types.d.ts +38 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +39 -0
- package/src/auditor.ts +100 -0
- package/src/auditors/accessibility.ts +2125 -0
- package/src/auditors/performance.ts +212 -0
- package/src/auditors/seo.ts +212 -0
- package/src/cli.ts +162 -0
- package/src/index.ts +22 -0
- package/src/parser/index.ts +106 -0
- package/src/parser/traverse.ts +14 -0
- package/src/reporter/terminal.ts +247 -0
- package/src/types.ts +51 -0
- package/tsconfig.json +47 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as parser from '@babel/parser';
|
|
4
|
+
import type { File } from '@babel/types';
|
|
5
|
+
|
|
6
|
+
// ─── Types ───────────────────────────────────────────────────────────────────
|
|
7
|
+
|
|
8
|
+
export interface ParsedFile {
|
|
9
|
+
filePath: string;
|
|
10
|
+
ast: File;
|
|
11
|
+
source: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// ─── Constants ───────────────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
const SUPPORTED_EXT = new Set(['.tsx', '.ts', '.jsx', '.js']);
|
|
17
|
+
|
|
18
|
+
// Directories we never descend into
|
|
19
|
+
const IGNORED_DIRS = new Set([
|
|
20
|
+
'node_modules', '.next', 'dist', 'build', '.git',
|
|
21
|
+
'.cache', 'coverage', 'out', '.turbo', 'storybook-static',
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
// ─── Public API ──────────────────────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Parse a single file with Babel.
|
|
28
|
+
* Returns null for unsupported extensions or parse errors (we never crash on
|
|
29
|
+
* a single bad file — we just skip it and continue).
|
|
30
|
+
*/
|
|
31
|
+
export function parseFile(filePath: string): ParsedFile | null {
|
|
32
|
+
if (!SUPPORTED_EXT.has(path.extname(filePath))) return null;
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const source = fs.readFileSync(filePath, 'utf-8');
|
|
36
|
+
const ast = parser.parse(source, {
|
|
37
|
+
sourceType: 'module',
|
|
38
|
+
// Enable all syntax that appears in real React/Next.js projects
|
|
39
|
+
plugins: [
|
|
40
|
+
'jsx',
|
|
41
|
+
'typescript',
|
|
42
|
+
'decorators-legacy',
|
|
43
|
+
'classProperties',
|
|
44
|
+
'classStaticBlock',
|
|
45
|
+
'optionalChaining',
|
|
46
|
+
'nullishCoalescingOperator',
|
|
47
|
+
'importAssertions',
|
|
48
|
+
],
|
|
49
|
+
});
|
|
50
|
+
return { filePath, ast, source };
|
|
51
|
+
} catch {
|
|
52
|
+
// Silently skip files that fail to parse.
|
|
53
|
+
// This happens with binary files, unusual encodings, or unsupported syntax.
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Recursively collect all supported source files under a target path.
|
|
60
|
+
* Target can be either a single file or a directory.
|
|
61
|
+
*/
|
|
62
|
+
export function collectFiles(target: string): string[] {
|
|
63
|
+
const absTarget = path.resolve(target);
|
|
64
|
+
|
|
65
|
+
let stat: fs.Stats;
|
|
66
|
+
try {
|
|
67
|
+
stat = fs.statSync(absTarget);
|
|
68
|
+
} catch {
|
|
69
|
+
throw new Error(`Path not found: ${absTarget}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (stat.isFile()) {
|
|
73
|
+
return SUPPORTED_EXT.has(path.extname(absTarget)) ? [absTarget] : [];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (stat.isDirectory()) {
|
|
77
|
+
return walkDir(absTarget);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// ─── Private helpers ─────────────────────────────────────────────────────────
|
|
84
|
+
|
|
85
|
+
function walkDir(dir: string): string[] {
|
|
86
|
+
const results: string[] = [];
|
|
87
|
+
|
|
88
|
+
for (const entry of fs.readdirSync(dir)) {
|
|
89
|
+
if (IGNORED_DIRS.has(entry)) continue;
|
|
90
|
+
|
|
91
|
+
const fullPath = path.join(dir, entry);
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
const entryStat = fs.statSync(fullPath);
|
|
95
|
+
if (entryStat.isDirectory()) {
|
|
96
|
+
results.push(...walkDir(fullPath));
|
|
97
|
+
} else if (SUPPORTED_EXT.has(path.extname(entry))) {
|
|
98
|
+
results.push(fullPath);
|
|
99
|
+
}
|
|
100
|
+
} catch {
|
|
101
|
+
// Skip files we can't stat (permission issues, broken symlinks, etc.)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return results;
|
|
106
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @babel/traverse has a well-known CommonJS interop quirk.
|
|
3
|
+
* Depending on the Node version and moduleResolution settings,
|
|
4
|
+
* the default export may land on `.default` or on the module itself.
|
|
5
|
+
* This file normalises that into a single reliable `traverse` export.
|
|
6
|
+
*
|
|
7
|
+
* All auditors import traverse from HERE, not directly from @babel/traverse.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import _traverse from '@babel/traverse';
|
|
11
|
+
|
|
12
|
+
export const traverse = ((typeof (_traverse as any).default === 'function'
|
|
13
|
+
? (_traverse as any).default
|
|
14
|
+
: _traverse) as any) as (ast: any, visitors: any) => any;
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import type { AuditReport, AuditResult, Issue, IssueImpact } from '../types.js';
|
|
3
|
+
|
|
4
|
+
// ─── Impact colour map ────────────────────────────────────────────────────────
|
|
5
|
+
|
|
6
|
+
const IMPACT_BADGE: Record<IssueImpact, string> = {
|
|
7
|
+
critical: chalk.bgRed.white.bold(' CRITICAL '),
|
|
8
|
+
major: chalk.bgYellow.black.bold(' MAJOR '),
|
|
9
|
+
minor: chalk.bgGray.white( ' MINOR '),
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// ─── Public API ───────────────────────────────────────────────────────────────
|
|
13
|
+
|
|
14
|
+
export function renderTerminal(report: AuditReport): void {
|
|
15
|
+
console.log('');
|
|
16
|
+
printHeader(report);
|
|
17
|
+
printScoreSummary(report);
|
|
18
|
+
|
|
19
|
+
const { results } = report;
|
|
20
|
+
if (results.performance) printCategorySection(results.performance);
|
|
21
|
+
if (results.seo) printCategorySection(results.seo);
|
|
22
|
+
if (results.accessibility) printCategorySection(results.accessibility);
|
|
23
|
+
|
|
24
|
+
printCriticalSummary(report);
|
|
25
|
+
printFooter(report);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// ─── Header ──────────────────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
function printHeader(report: AuditReport): void {
|
|
31
|
+
const W = 66;
|
|
32
|
+
const rule = '─'.repeat(W);
|
|
33
|
+
|
|
34
|
+
const centre = (text: string) => {
|
|
35
|
+
const pad = Math.max(0, Math.floor((W - text.length) / 2));
|
|
36
|
+
return ' '.repeat(pad) + text + ' '.repeat(Math.max(0, W - pad - text.length));
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
console.log(chalk.cyan(`┌${rule}┐`));
|
|
40
|
+
console.log(chalk.cyan('│') + chalk.bold(centre('🔍 UIAudit')) + chalk.cyan('│'));
|
|
41
|
+
console.log(chalk.cyan('│') + chalk.cyan(centre(truncate(report.target, W - 2))) + chalk.cyan('│'));
|
|
42
|
+
console.log(chalk.cyan('│') + chalk.dim(centre(new Date(report.timestamp).toLocaleString())) + chalk.cyan('│'));
|
|
43
|
+
console.log(chalk.cyan(`└${rule}┘`));
|
|
44
|
+
console.log('');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ─── Score summary table ──────────────────────────────────────────────────────
|
|
48
|
+
|
|
49
|
+
function printScoreSummary(report: AuditReport): void {
|
|
50
|
+
const { overallScore, totalFiles, results } = report;
|
|
51
|
+
|
|
52
|
+
console.log(
|
|
53
|
+
`${chalk.bold('Overall Score:')} ${colorScore(overallScore)(`${overallScore}/100`)} ${scoreLabel(overallScore)}` +
|
|
54
|
+
chalk.dim(` (${totalFiles} file${totalFiles !== 1 ? 's' : ''} scanned)\n`)
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const rows: [string, AuditResult | undefined][] = [
|
|
58
|
+
['Performance', results.performance],
|
|
59
|
+
['SEO', results.seo],
|
|
60
|
+
['Accessibility', results.accessibility],
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
for (const [label, result] of rows) {
|
|
64
|
+
if (!result) continue;
|
|
65
|
+
const { score, counts } = result;
|
|
66
|
+
|
|
67
|
+
const bar = scoreBar(score);
|
|
68
|
+
const scoreStr = colorScore(score)(`${score}/100`.padEnd(8));
|
|
69
|
+
const summary =
|
|
70
|
+
counts.total === 0
|
|
71
|
+
? chalk.green('No issues ✓')
|
|
72
|
+
: [
|
|
73
|
+
counts.critical ? chalk.red.bold(`${counts.critical} critical`) : '',
|
|
74
|
+
counts.major ? chalk.yellow(`${counts.major} major`) : '',
|
|
75
|
+
counts.minor ? chalk.gray(`${counts.minor} minor`) : '',
|
|
76
|
+
]
|
|
77
|
+
.filter(Boolean)
|
|
78
|
+
.join(chalk.dim(' '));
|
|
79
|
+
|
|
80
|
+
console.log(` ${chalk.bold(label.padEnd(15))} ${scoreStr} ${bar} ${summary}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
console.log('');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ─── Category section ─────────────────────────────────────────────────────────
|
|
87
|
+
|
|
88
|
+
function printCategorySection(result: AuditResult): void {
|
|
89
|
+
const CATEGORY_COLORS: Record<string, chalk.ChalkFunction> = {
|
|
90
|
+
performance: chalk.blue.bold,
|
|
91
|
+
seo: chalk.green.bold,
|
|
92
|
+
accessibility: chalk.magenta.bold,
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const color = CATEGORY_COLORS[result.category] ?? chalk.bold;
|
|
96
|
+
const title = result.category.toUpperCase();
|
|
97
|
+
|
|
98
|
+
console.log(color('═'.repeat(62)));
|
|
99
|
+
console.log(
|
|
100
|
+
color(` ${title}`) +
|
|
101
|
+
color(' · ') +
|
|
102
|
+
colorScore(result.score)(`${result.score}/100`)
|
|
103
|
+
);
|
|
104
|
+
console.log(color('═'.repeat(62)));
|
|
105
|
+
console.log('');
|
|
106
|
+
|
|
107
|
+
if (result.issues.length === 0) {
|
|
108
|
+
console.log(` ${chalk.green('✅')} No issues found.\n`);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Print issues grouped by impact level (critical → major → minor)
|
|
113
|
+
const groups: IssueImpact[] = ['critical', 'major', 'minor'];
|
|
114
|
+
for (const impact of groups) {
|
|
115
|
+
const group = result.issues.filter((i) => i.impact === impact);
|
|
116
|
+
if (group.length === 0) continue;
|
|
117
|
+
|
|
118
|
+
for (const issue of group) {
|
|
119
|
+
printIssue(issue);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
console.log('');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ─── Single issue block ───────────────────────────────────────────────────────
|
|
127
|
+
|
|
128
|
+
function printIssue(issue: Issue): void {
|
|
129
|
+
const statusIcon = issue.status === 'fail' ? '❌' : '⚠️ ';
|
|
130
|
+
const badge = IMPACT_BADGE[issue.impact];
|
|
131
|
+
|
|
132
|
+
// Location string (file:line)
|
|
133
|
+
const location = issue.file && issue.line
|
|
134
|
+
? chalk.dim(` ${shortPath(issue.file)}:${issue.line}`)
|
|
135
|
+
: '';
|
|
136
|
+
|
|
137
|
+
console.log(` ${statusIcon} ${badge} ${chalk.bold(issue.title)}${location}`);
|
|
138
|
+
console.log(` ${chalk.dim(issue.description)}`);
|
|
139
|
+
console.log('');
|
|
140
|
+
|
|
141
|
+
// Code snippet (what's wrong)
|
|
142
|
+
if (issue.codeSnippet) {
|
|
143
|
+
console.log(` ${chalk.red.dim('✗')} ${chalk.red.dim(issue.codeSnippet)}`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Fix snippet (what to write instead)
|
|
147
|
+
if (issue.fixSnippet) {
|
|
148
|
+
console.log(` ${chalk.green.dim('✓')} ${chalk.green(issue.fixSnippet)}`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Multi-line suggestion
|
|
152
|
+
console.log('');
|
|
153
|
+
const suggestionLines = issue.suggestion.split('\n');
|
|
154
|
+
console.log(` ${chalk.cyan('💡 Fix:')} ${suggestionLines[0]}`);
|
|
155
|
+
for (const line of suggestionLines.slice(1)) {
|
|
156
|
+
console.log(` ${chalk.dim(line)}`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
console.log('');
|
|
160
|
+
console.log(' ' + chalk.dim('─'.repeat(54)));
|
|
161
|
+
console.log('');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// ─── Critical summary (always shown at the end if any critical exist) ─────────
|
|
165
|
+
|
|
166
|
+
function printCriticalSummary(report: AuditReport): void {
|
|
167
|
+
const criticals: Issue[] = [];
|
|
168
|
+
|
|
169
|
+
for (const result of Object.values(report.results)) {
|
|
170
|
+
if (!result) continue;
|
|
171
|
+
criticals.push(...result.issues.filter((i) => i.impact === 'critical'));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (criticals.length === 0) return;
|
|
175
|
+
|
|
176
|
+
console.log(chalk.red.bold(`┌─ 🚨 Fix These First (${criticals.length} critical issue${criticals.length !== 1 ? 's' : ''}) ${'─'.repeat(25)}┐`));
|
|
177
|
+
console.log('');
|
|
178
|
+
|
|
179
|
+
criticals.slice(0, 6).forEach((issue, idx) => {
|
|
180
|
+
const cat = issue.category.padEnd(13);
|
|
181
|
+
console.log(
|
|
182
|
+
` ${chalk.red.bold(String(idx + 1) + '.')} ` +
|
|
183
|
+
chalk.dim(`[${cat}]`) + ' ' +
|
|
184
|
+
chalk.bold(issue.title)
|
|
185
|
+
);
|
|
186
|
+
if (issue.file) {
|
|
187
|
+
console.log(` ${chalk.dim(shortPath(issue.file))}${issue.line ? chalk.dim(`:${issue.line}`) : ''}`);
|
|
188
|
+
}
|
|
189
|
+
console.log('');
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
console.log(chalk.red.bold('└' + '─'.repeat(58) + '┘'));
|
|
193
|
+
console.log('');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// ─── Footer ───────────────────────────────────────────────────────────────────
|
|
197
|
+
|
|
198
|
+
function printFooter(report: AuditReport): void {
|
|
199
|
+
const totalIssues = Object.values(report.results)
|
|
200
|
+
.filter(Boolean)
|
|
201
|
+
.reduce((sum, r) => sum + r!.counts.total, 0);
|
|
202
|
+
|
|
203
|
+
if (totalIssues === 0) {
|
|
204
|
+
console.log(chalk.green.bold(' ✅ All checks passed. Your code looks good!\n'));
|
|
205
|
+
} else {
|
|
206
|
+
console.log(
|
|
207
|
+
chalk.dim(
|
|
208
|
+
` Found ${totalIssues} issue${totalIssues !== 1 ? 's' : ''} across ` +
|
|
209
|
+
`${report.totalFiles} file${report.totalFiles !== 1 ? 's' : ''}. ` +
|
|
210
|
+
`Run with --output json to export the full report.\n`
|
|
211
|
+
)
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
217
|
+
|
|
218
|
+
function scoreBar(score: number): string {
|
|
219
|
+
const filled = Math.round(score / 10);
|
|
220
|
+
const empty = 10 - filled;
|
|
221
|
+
const color = score >= 90 ? chalk.green : score >= 70 ? chalk.yellow : chalk.red;
|
|
222
|
+
return color('█'.repeat(filled)) + chalk.gray('░'.repeat(empty));
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function colorScore(score: number): chalk.ChalkFunction {
|
|
226
|
+
if (score >= 90) return chalk.green.bold;
|
|
227
|
+
if (score >= 70) return chalk.yellow.bold;
|
|
228
|
+
if (score >= 50) return chalk.yellow;
|
|
229
|
+
return chalk.red.bold;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function scoreLabel(score: number): string {
|
|
233
|
+
if (score >= 90) return chalk.green('Excellent ✓');
|
|
234
|
+
if (score >= 70) return chalk.yellow('Good');
|
|
235
|
+
if (score >= 50) return chalk.yellow('Needs work');
|
|
236
|
+
return chalk.red.bold('Critical issues found');
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/** Show only the last 3 path segments to keep output readable. */
|
|
240
|
+
function shortPath(filePath: string): string {
|
|
241
|
+
const parts = filePath.replace(/\\/g, '/').split('/');
|
|
242
|
+
return (parts.length > 3 ? '…/' : '') + parts.slice(-3).join('/');
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function truncate(str: string, maxLen: number): string {
|
|
246
|
+
return str.length > maxLen ? '…' + str.slice(-(maxLen - 1)) : str;
|
|
247
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// ─── Audit Categories ────────────────────────────────────────────────────────
|
|
2
|
+
|
|
3
|
+
export type AuditCategory = 'performance' | 'seo' | 'accessibility';
|
|
4
|
+
export type IssueImpact = 'critical' | 'major' | 'minor';
|
|
5
|
+
export type IssueStatus = 'fail' | 'warning';
|
|
6
|
+
|
|
7
|
+
// ─── A single issue found in a file ─────────────────────────────────────────
|
|
8
|
+
|
|
9
|
+
export interface Issue {
|
|
10
|
+
id: string;
|
|
11
|
+
category: AuditCategory;
|
|
12
|
+
title: string;
|
|
13
|
+
description: string;
|
|
14
|
+
impact: IssueImpact;
|
|
15
|
+
status: IssueStatus;
|
|
16
|
+
suggestion: string; // Plain-text fix explanation
|
|
17
|
+
codeSnippet?: string; // The problematic pattern
|
|
18
|
+
fixSnippet?: string; // The corrected version
|
|
19
|
+
file?: string; // Absolute path
|
|
20
|
+
line?: number; // Line number in the source file
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// ─── Result for a single audit category ─────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
export interface AuditResult {
|
|
26
|
+
category: AuditCategory;
|
|
27
|
+
score: number; // 0–100
|
|
28
|
+
issues: Issue[];
|
|
29
|
+
counts: {
|
|
30
|
+
critical: number;
|
|
31
|
+
major: number;
|
|
32
|
+
minor: number;
|
|
33
|
+
total: number;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ─── The full report returned by runAudit() ──────────────────────────────────
|
|
38
|
+
|
|
39
|
+
export interface AuditReport {
|
|
40
|
+
target: string; // The path that was audited
|
|
41
|
+
timestamp: string; // ISO 8601
|
|
42
|
+
overallScore: number; // Weighted average of category scores
|
|
43
|
+
totalFiles: number; // How many .tsx/.ts/.jsx/.js files were scanned
|
|
44
|
+
results: Partial<Record<AuditCategory, AuditResult>>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ─── Options passed in from CLI or programmatic API ─────────────────────────
|
|
48
|
+
|
|
49
|
+
export interface AuditOptions {
|
|
50
|
+
types: AuditCategory[];
|
|
51
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
// Visit https://aka.ms/tsconfig to read more about this file
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
// File Layout
|
|
5
|
+
"rootDir": "./src",
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
// Environment Settings
|
|
9
|
+
// See also https://aka.ms/tsconfig/module
|
|
10
|
+
|
|
11
|
+
"module": "Node16",
|
|
12
|
+
"moduleResolution": "node16",
|
|
13
|
+
"target": "es2020",
|
|
14
|
+
"types": ["node"],
|
|
15
|
+
// For nodejs:
|
|
16
|
+
// "lib": ["esnext"],
|
|
17
|
+
// "types": ["node"],
|
|
18
|
+
// and npm install -D @types/node
|
|
19
|
+
|
|
20
|
+
// Other Outputs
|
|
21
|
+
"sourceMap": true,
|
|
22
|
+
"declaration": true,
|
|
23
|
+
"declarationMap": true,
|
|
24
|
+
"emitDeclarationOnly": false,
|
|
25
|
+
|
|
26
|
+
// Stricter Typechecking Options
|
|
27
|
+
"noUncheckedIndexedAccess": true,
|
|
28
|
+
"exactOptionalPropertyTypes": true,
|
|
29
|
+
|
|
30
|
+
// Style Options
|
|
31
|
+
// "noImplicitReturns": true,
|
|
32
|
+
// "noImplicitOverride": true,
|
|
33
|
+
// "noUnusedLocals": true,
|
|
34
|
+
// "noUnusedParameters": true,
|
|
35
|
+
// "noFallthroughCasesInSwitch": true,
|
|
36
|
+
// "noPropertyAccessFromIndexSignature": true,
|
|
37
|
+
|
|
38
|
+
// Recommended Options
|
|
39
|
+
"strict": true,
|
|
40
|
+
"jsx": "react-jsx",
|
|
41
|
+
"verbatimModuleSyntax": true,
|
|
42
|
+
"isolatedModules": true,
|
|
43
|
+
"noUncheckedSideEffectImports": true,
|
|
44
|
+
"moduleDetection": "force",
|
|
45
|
+
"skipLibCheck": true,
|
|
46
|
+
}
|
|
47
|
+
}
|