@buoy-design/core 0.3.32 → 0.3.34
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/analysis/audit.d.sync-conflict-20260306-131742-6PCZ3ZU.ts +141 -0
- package/dist/analysis/audit.js.sync-conflict-20260309-150631-6PCZ3ZU.map +1 -0
- package/dist/analysis/audit.sync-conflict-20260305-235746-6PCZ3ZU.js +498 -0
- package/dist/analysis/audit.sync-conflict-20260306-131031-6PCZ3ZU.d.ts +141 -0
- package/dist/analysis/audit.sync-conflict-20260306-131031-6PCZ3ZU.d.ts.map +1 -0
- package/dist/analysis/audit.sync-conflict-20260306-131031-6PCZ3ZU.js +498 -0
- package/dist/analysis/audit.sync-conflict-20260306-131031-6PCZ3ZU.js.map +1 -0
- package/dist/analysis/semantic-diff.d.ts.sync-conflict-20260305-144725-6PCZ3ZU.map +1 -0
- package/dist/analysis/semantic-diff.js.sync-conflict-20260307-172619-6PCZ3ZU.map +1 -0
- package/package.json +10 -10
- package/LICENSE +0 -21
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
export interface AuditValue {
|
|
2
|
+
category: 'color' | 'spacing' | 'typography' | 'radius';
|
|
3
|
+
value: string;
|
|
4
|
+
file: string;
|
|
5
|
+
line: number;
|
|
6
|
+
}
|
|
7
|
+
export interface CategoryStats {
|
|
8
|
+
uniqueCount: number;
|
|
9
|
+
totalUsages: number;
|
|
10
|
+
mostCommon: Array<{
|
|
11
|
+
value: string;
|
|
12
|
+
count: number;
|
|
13
|
+
}>;
|
|
14
|
+
}
|
|
15
|
+
export interface FileIssue {
|
|
16
|
+
file: string;
|
|
17
|
+
issueCount: number;
|
|
18
|
+
}
|
|
19
|
+
export interface CloseMatch {
|
|
20
|
+
value: string;
|
|
21
|
+
closeTo: string;
|
|
22
|
+
distance: number;
|
|
23
|
+
}
|
|
24
|
+
export interface AuditReport {
|
|
25
|
+
categories: Record<string, CategoryStats>;
|
|
26
|
+
worstFiles: FileIssue[];
|
|
27
|
+
totals: {
|
|
28
|
+
uniqueValues: number;
|
|
29
|
+
totalUsages: number;
|
|
30
|
+
filesAffected: number;
|
|
31
|
+
};
|
|
32
|
+
closeMatches: CloseMatch[];
|
|
33
|
+
score: number;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Metrics gathered from drift analysis for health scoring.
|
|
37
|
+
* All counts come from drift signals and scan results.
|
|
38
|
+
*/
|
|
39
|
+
export interface HealthMetrics {
|
|
40
|
+
/** Total components found in codebase */
|
|
41
|
+
componentCount: number;
|
|
42
|
+
/** Total design tokens defined */
|
|
43
|
+
tokenCount: number;
|
|
44
|
+
/** Number of hardcoded-value drift signals */
|
|
45
|
+
hardcodedValueCount: number;
|
|
46
|
+
/** Number of unused-token drift signals */
|
|
47
|
+
unusedTokenCount: number;
|
|
48
|
+
/** Number of naming-inconsistency drift signals */
|
|
49
|
+
namingInconsistencyCount: number;
|
|
50
|
+
/** Number of critical-severity drift signals */
|
|
51
|
+
criticalCount: number;
|
|
52
|
+
/** Whether a utility CSS framework (Tailwind) is detected */
|
|
53
|
+
hasUtilityFramework: boolean;
|
|
54
|
+
/** Whether a design system library (MUI, Chakra, shadcn, etc.) is detected */
|
|
55
|
+
hasDesignSystemLibrary: boolean;
|
|
56
|
+
/** Total drift signals of ALL types (hardcoded-value, naming-inconsistency, repeated-pattern, etc.) */
|
|
57
|
+
totalDriftCount?: number;
|
|
58
|
+
/** Number of unused-component drift signals (dead code) */
|
|
59
|
+
unusedComponentCount?: number;
|
|
60
|
+
/** Number of repeated-pattern drift signals (extract to shared component) */
|
|
61
|
+
repeatedPatternCount?: number;
|
|
62
|
+
/** Number of orphaned-component drift signals (dead code) */
|
|
63
|
+
orphanedComponentCount?: number;
|
|
64
|
+
/** Number of semantic-mismatch drift signals (naming/structure inconsistencies) */
|
|
65
|
+
semanticMismatchCount?: number;
|
|
66
|
+
/** Number of deprecated-pattern drift signals (technical debt) */
|
|
67
|
+
deprecatedPatternCount?: number;
|
|
68
|
+
/** Number of files with >2 hardcoded values (severe maintenance burden) */
|
|
69
|
+
highDensityFileCount?: number;
|
|
70
|
+
/** Number of drift signals from vendored/template components (e.g., shadcn/ui) */
|
|
71
|
+
vendoredDriftCount?: number;
|
|
72
|
+
/** Most common hardcoded color (for suggestions) */
|
|
73
|
+
topHardcodedColor?: {
|
|
74
|
+
value: string;
|
|
75
|
+
count: number;
|
|
76
|
+
};
|
|
77
|
+
/** File with the most drift issues */
|
|
78
|
+
worstFile?: {
|
|
79
|
+
path: string;
|
|
80
|
+
issueCount: number;
|
|
81
|
+
};
|
|
82
|
+
/** Total unique spacing values found */
|
|
83
|
+
uniqueSpacingValues?: number;
|
|
84
|
+
/** Names of detected frameworks/libraries (for framework-aware suggestions) */
|
|
85
|
+
detectedFrameworkNames?: string[];
|
|
86
|
+
}
|
|
87
|
+
export interface HealthPillar {
|
|
88
|
+
name: string;
|
|
89
|
+
score: number;
|
|
90
|
+
maxScore: number;
|
|
91
|
+
description: string;
|
|
92
|
+
}
|
|
93
|
+
export interface HealthScoreResult {
|
|
94
|
+
/** Overall score 0-100, or null if no UI surface detected */
|
|
95
|
+
score: number | null;
|
|
96
|
+
/** Tier label */
|
|
97
|
+
tier: 'Great' | 'Good' | 'OK' | 'Bad' | 'Terrible' | 'N/A';
|
|
98
|
+
/** Individual pillar scores */
|
|
99
|
+
pillars: {
|
|
100
|
+
valueDiscipline: HealthPillar;
|
|
101
|
+
tokenHealth: HealthPillar;
|
|
102
|
+
consistency: HealthPillar;
|
|
103
|
+
criticalIssues: HealthPillar;
|
|
104
|
+
};
|
|
105
|
+
/** Actionable improvement suggestions */
|
|
106
|
+
suggestions: string[];
|
|
107
|
+
/** Raw metrics used for scoring */
|
|
108
|
+
metrics: HealthMetrics;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Generate an audit report from extracted values
|
|
112
|
+
*/
|
|
113
|
+
export declare function generateAuditReport(values: AuditValue[]): AuditReport;
|
|
114
|
+
/**
|
|
115
|
+
* Find values that are close to design tokens (likely typos)
|
|
116
|
+
*/
|
|
117
|
+
export declare function findCloseMatches(foundValues: string[], designTokens: string[], category: 'color' | 'spacing' | 'typography' | 'radius'): CloseMatch[];
|
|
118
|
+
/**
|
|
119
|
+
* @deprecated Use calculateHealthScorePillar() with proper HealthMetrics instead.
|
|
120
|
+
* This legacy function approximates componentCount from filesAffected which
|
|
121
|
+
* is inaccurate. Kept for backward compatibility with external callers.
|
|
122
|
+
*/
|
|
123
|
+
export declare function calculateHealthScore(report: AuditReport): number;
|
|
124
|
+
/**
|
|
125
|
+
* 4-Pillar Health Score System
|
|
126
|
+
*
|
|
127
|
+
* Measures design system health across four dimensions:
|
|
128
|
+
* - Value Discipline (0-60): Hardcoded values per component (density)
|
|
129
|
+
* - Token Health (0-20): Token system existence and adoption
|
|
130
|
+
* - Consistency (0-10): Naming convention adherence
|
|
131
|
+
* - Critical Issues (0-10): Accessibility and critical failures
|
|
132
|
+
*
|
|
133
|
+
* Tiers: 80-100 Great, 60-79 Good, 40-59 OK, 20-39 Bad, 0-19 Terrible
|
|
134
|
+
*
|
|
135
|
+
* Note: A perfect 100 requires maxing all 4 pillars simultaneously.
|
|
136
|
+
* In practice, the highest-scoring real-world apps reach ~95.
|
|
137
|
+
* A score of 95 represents near-perfect design system health.
|
|
138
|
+
*/
|
|
139
|
+
export declare function calculateHealthScorePillar(metrics: HealthMetrics): HealthScoreResult;
|
|
140
|
+
export declare function getHealthTier(score: number): 'Great' | 'Good' | 'OK' | 'Bad' | 'Terrible';
|
|
141
|
+
//# sourceMappingURL=audit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/analysis/audit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
// Audit report generation - analyzes codebase for design system health
|
|
2
|
+
/**
|
|
3
|
+
* Generate an audit report from extracted values
|
|
4
|
+
*/
|
|
5
|
+
export function generateAuditReport(values) {
|
|
6
|
+
if (values.length === 0) {
|
|
7
|
+
return {
|
|
8
|
+
categories: {},
|
|
9
|
+
worstFiles: [],
|
|
10
|
+
totals: { uniqueValues: 0, totalUsages: 0, filesAffected: 0 },
|
|
11
|
+
closeMatches: [],
|
|
12
|
+
score: 100,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
// Group by category
|
|
16
|
+
const byCategory = new Map();
|
|
17
|
+
const byFile = new Map();
|
|
18
|
+
const allFiles = new Set();
|
|
19
|
+
for (const v of values) {
|
|
20
|
+
// Category stats
|
|
21
|
+
if (!byCategory.has(v.category)) {
|
|
22
|
+
byCategory.set(v.category, new Map());
|
|
23
|
+
}
|
|
24
|
+
const catMap = byCategory.get(v.category);
|
|
25
|
+
catMap.set(v.value, (catMap.get(v.value) || 0) + 1);
|
|
26
|
+
// File stats
|
|
27
|
+
byFile.set(v.file, (byFile.get(v.file) || 0) + 1);
|
|
28
|
+
allFiles.add(v.file);
|
|
29
|
+
}
|
|
30
|
+
// Build category stats
|
|
31
|
+
const categories = {};
|
|
32
|
+
let totalUnique = 0;
|
|
33
|
+
for (const [category, valueMap] of byCategory) {
|
|
34
|
+
const entries = [...valueMap.entries()];
|
|
35
|
+
const uniqueCount = entries.length;
|
|
36
|
+
const totalUsages = entries.reduce((sum, [, count]) => sum + count, 0);
|
|
37
|
+
// Sort by count descending for mostCommon
|
|
38
|
+
const mostCommon = entries
|
|
39
|
+
.map(([value, count]) => ({ value, count }))
|
|
40
|
+
.sort((a, b) => b.count - a.count)
|
|
41
|
+
.slice(0, 10);
|
|
42
|
+
categories[category] = { uniqueCount, totalUsages, mostCommon };
|
|
43
|
+
totalUnique += uniqueCount;
|
|
44
|
+
}
|
|
45
|
+
// Build worst files list
|
|
46
|
+
const worstFiles = [...byFile.entries()]
|
|
47
|
+
.map(([file, issueCount]) => ({ file, issueCount }))
|
|
48
|
+
.sort((a, b) => b.issueCount - a.issueCount)
|
|
49
|
+
.slice(0, 10);
|
|
50
|
+
const report = {
|
|
51
|
+
categories,
|
|
52
|
+
worstFiles,
|
|
53
|
+
totals: {
|
|
54
|
+
uniqueValues: totalUnique,
|
|
55
|
+
totalUsages: values.length,
|
|
56
|
+
filesAffected: allFiles.size,
|
|
57
|
+
},
|
|
58
|
+
closeMatches: [],
|
|
59
|
+
score: 0,
|
|
60
|
+
};
|
|
61
|
+
report.score = calculateHealthScore(report);
|
|
62
|
+
return report;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Find values that are close to design tokens (likely typos)
|
|
66
|
+
*/
|
|
67
|
+
export function findCloseMatches(foundValues, designTokens, category) {
|
|
68
|
+
const matches = [];
|
|
69
|
+
const tokenSet = new Set(designTokens.map((t) => t.toLowerCase()));
|
|
70
|
+
for (const value of foundValues) {
|
|
71
|
+
const valueLower = value.toLowerCase();
|
|
72
|
+
// Skip exact matches
|
|
73
|
+
if (tokenSet.has(valueLower)) {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
// Find closest token
|
|
77
|
+
let closestToken = null;
|
|
78
|
+
let closestDistance = Infinity;
|
|
79
|
+
for (const token of designTokens) {
|
|
80
|
+
const distance = getDistance(value, token, category);
|
|
81
|
+
if (distance < closestDistance && distance > 0) {
|
|
82
|
+
closestDistance = distance;
|
|
83
|
+
closestToken = token;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Only include if close enough (threshold depends on category)
|
|
87
|
+
const threshold = category === 'color' ? 5 : 2;
|
|
88
|
+
if (closestToken && closestDistance <= threshold) {
|
|
89
|
+
matches.push({
|
|
90
|
+
value,
|
|
91
|
+
closeTo: closestToken,
|
|
92
|
+
distance: closestDistance,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return matches;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Calculate distance between two values
|
|
100
|
+
*/
|
|
101
|
+
function getDistance(a, b, category) {
|
|
102
|
+
if (category === 'color') {
|
|
103
|
+
return colorDistance(a, b);
|
|
104
|
+
}
|
|
105
|
+
if (category === 'spacing' || category === 'radius') {
|
|
106
|
+
return numericDistance(a, b);
|
|
107
|
+
}
|
|
108
|
+
// For typography, use simple string comparison
|
|
109
|
+
return a.toLowerCase() === b.toLowerCase() ? 0 : Infinity;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Calculate color distance (simple hex comparison)
|
|
113
|
+
*/
|
|
114
|
+
function colorDistance(a, b) {
|
|
115
|
+
const hexA = a.replace('#', '').toLowerCase();
|
|
116
|
+
const hexB = b.replace('#', '').toLowerCase();
|
|
117
|
+
if (hexA.length !== 6 || hexB.length !== 6) {
|
|
118
|
+
return Infinity;
|
|
119
|
+
}
|
|
120
|
+
// Count differing characters
|
|
121
|
+
let diff = 0;
|
|
122
|
+
for (let i = 0; i < 6; i++) {
|
|
123
|
+
if (hexA[i] !== hexB[i]) {
|
|
124
|
+
diff++;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return diff;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Calculate numeric distance for spacing/radius
|
|
131
|
+
*/
|
|
132
|
+
function numericDistance(a, b) {
|
|
133
|
+
const numA = parseFloat(a);
|
|
134
|
+
const numB = parseFloat(b);
|
|
135
|
+
if (isNaN(numA) || isNaN(numB)) {
|
|
136
|
+
return Infinity;
|
|
137
|
+
}
|
|
138
|
+
return Math.abs(numA - numB);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* @deprecated Use calculateHealthScorePillar() with proper HealthMetrics instead.
|
|
142
|
+
* This legacy function approximates componentCount from filesAffected which
|
|
143
|
+
* is inaccurate. Kept for backward compatibility with external callers.
|
|
144
|
+
*/
|
|
145
|
+
export function calculateHealthScore(report) {
|
|
146
|
+
const metrics = {
|
|
147
|
+
componentCount: report.totals.filesAffected || 1,
|
|
148
|
+
tokenCount: 0,
|
|
149
|
+
hardcodedValueCount: report.totals.uniqueValues,
|
|
150
|
+
unusedTokenCount: 0,
|
|
151
|
+
namingInconsistencyCount: 0,
|
|
152
|
+
criticalCount: 0,
|
|
153
|
+
hasUtilityFramework: false,
|
|
154
|
+
hasDesignSystemLibrary: false,
|
|
155
|
+
};
|
|
156
|
+
return calculateHealthScorePillar(metrics).score ?? 0;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* 4-Pillar Health Score System
|
|
160
|
+
*
|
|
161
|
+
* Measures design system health across four dimensions:
|
|
162
|
+
* - Value Discipline (0-60): Hardcoded values per component (density)
|
|
163
|
+
* - Token Health (0-20): Token system existence and adoption
|
|
164
|
+
* - Consistency (0-10): Naming convention adherence
|
|
165
|
+
* - Critical Issues (0-10): Accessibility and critical failures
|
|
166
|
+
*
|
|
167
|
+
* Tiers: 80-100 Great, 60-79 Good, 40-59 OK, 20-39 Bad, 0-19 Terrible
|
|
168
|
+
*
|
|
169
|
+
* Note: A perfect 100 requires maxing all 4 pillars simultaneously.
|
|
170
|
+
* In practice, the highest-scoring real-world apps reach ~95.
|
|
171
|
+
* A score of 95 represents near-perfect design system health.
|
|
172
|
+
*/
|
|
173
|
+
export function calculateHealthScorePillar(metrics) {
|
|
174
|
+
const suggestions = [];
|
|
175
|
+
const frameworks = metrics.detectedFrameworkNames ?? [];
|
|
176
|
+
// No UI surface area — can't evaluate design system health
|
|
177
|
+
if (metrics.componentCount === 0 && metrics.tokenCount === 0 && (metrics.totalDriftCount ?? 0) === 0) {
|
|
178
|
+
return {
|
|
179
|
+
score: null,
|
|
180
|
+
tier: 'N/A',
|
|
181
|
+
pillars: {
|
|
182
|
+
valueDiscipline: { name: 'Value Discipline', score: 0, maxScore: 60, description: 'Hardcoded values per component' },
|
|
183
|
+
tokenHealth: { name: 'Token Health', score: 0, maxScore: 20, description: 'Token system adoption' },
|
|
184
|
+
consistency: { name: 'Consistency', score: 0, maxScore: 10, description: 'Naming convention adherence' },
|
|
185
|
+
criticalIssues: { name: 'Critical Issues', score: 0, maxScore: 10, description: 'Accessibility and critical failures' },
|
|
186
|
+
},
|
|
187
|
+
suggestions: ['No UI components or design tokens detected — this repo may not need design system health tracking'],
|
|
188
|
+
metrics,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
// Pillar 1: Value Discipline (0-60)
|
|
192
|
+
// Primary: hardcoded value density
|
|
193
|
+
// Secondary: dead code density (unused/orphaned components, repeated patterns)
|
|
194
|
+
// Tertiary: total drift density as backstop
|
|
195
|
+
const userHardcodedCount = Math.max(0, metrics.hardcodedValueCount - (metrics.vendoredDriftCount ?? 0));
|
|
196
|
+
const hardcodedDensity = userHardcodedCount / Math.max(metrics.componentCount, 1);
|
|
197
|
+
const deadCodeCount = (metrics.unusedComponentCount ?? 0)
|
|
198
|
+
+ (metrics.orphanedComponentCount ?? 0)
|
|
199
|
+
+ (metrics.repeatedPatternCount ?? 0);
|
|
200
|
+
const deadCodeDensity = deadCodeCount / Math.max(metrics.componentCount, 1);
|
|
201
|
+
const totalDriftDensity = (metrics.totalDriftCount ?? metrics.hardcodedValueCount) / Math.max(metrics.componentCount, 1);
|
|
202
|
+
// Hardcoded density is primary; dead code adds 30% partial penalty; total drift as backstop
|
|
203
|
+
const density = Math.max(hardcodedDensity + deadCodeDensity * 0.3, totalDriftDensity * 0.5);
|
|
204
|
+
// Treat near-zero density as perfect: repos with <0.1 hardcoded values per component
|
|
205
|
+
// are effectively clean (e.g., 193 components with 12 hardcoded values = 0.06/comp)
|
|
206
|
+
const effectiveDensity = density < 0.1 ? 0 : density;
|
|
207
|
+
// Continuous scoring (no rounding) — round only the final total for more granular scores
|
|
208
|
+
const valueDisciplineRaw = 60 * clamp(1 - effectiveDensity / 2, 0, 1);
|
|
209
|
+
const valueDisciplineScore = Math.round(valueDisciplineRaw);
|
|
210
|
+
if (metrics.hardcodedValueCount > 0) {
|
|
211
|
+
const hasTailwind = frameworks.includes('tailwind');
|
|
212
|
+
const hasShadcn = frameworks.includes('shadcn');
|
|
213
|
+
const hasMui = frameworks.includes('mui');
|
|
214
|
+
let suggestion;
|
|
215
|
+
if (density > 1.0) {
|
|
216
|
+
// Severe — high urgency
|
|
217
|
+
suggestion = `${userHardcodedCount} hardcoded values across your components — high density`;
|
|
218
|
+
if (metrics.topHardcodedColor) {
|
|
219
|
+
suggestion += `. Most common: ${metrics.topHardcodedColor.value} (${metrics.topHardcodedColor.count}\u00d7)`;
|
|
220
|
+
}
|
|
221
|
+
if (hasTailwind) {
|
|
222
|
+
suggestion += '. Add values to your Tailwind theme config instead of using arbitrary values';
|
|
223
|
+
}
|
|
224
|
+
else if (hasMui) {
|
|
225
|
+
suggestion += '. Use the `sx` prop or `theme.palette` instead of inline colors';
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
suggestion += '. Create a design token file (e.g., tokens.css with CSS custom properties)';
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
else if (density > 0.3) {
|
|
232
|
+
// Moderate
|
|
233
|
+
suggestion = `${userHardcodedCount} hardcoded value${userHardcodedCount === 1 ? '' : 's'} to extract`;
|
|
234
|
+
if (hasTailwind && hasShadcn) {
|
|
235
|
+
suggestion += ' — use `cn()` utility with Tailwind theme classes';
|
|
236
|
+
}
|
|
237
|
+
else if (hasTailwind) {
|
|
238
|
+
suggestion += ' — extend your tailwind.config theme instead of arbitrary values';
|
|
239
|
+
}
|
|
240
|
+
if (metrics.worstFile) {
|
|
241
|
+
suggestion += `. Focus on ${metrics.worstFile.path} (${metrics.worstFile.issueCount} issues)`;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
// Low — encouraging
|
|
246
|
+
suggestion = `Nearly there — just ${userHardcodedCount} hardcoded value${userHardcodedCount === 1 ? '' : 's'} left to tokenize`;
|
|
247
|
+
if (metrics.worstFile && metrics.worstFile.issueCount > 1) {
|
|
248
|
+
suggestion += `. ${metrics.worstFile.path} has the most (${metrics.worstFile.issueCount})`;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
suggestions.push(suggestion);
|
|
252
|
+
}
|
|
253
|
+
if ((metrics.vendoredDriftCount ?? 0) > 0) {
|
|
254
|
+
suggestions.push(`${metrics.vendoredDriftCount} additional hardcoded values in vendored components (shadcn/ui templates) — these are from the library, not your code`);
|
|
255
|
+
}
|
|
256
|
+
// Dead code suggestions (threshold-based to reduce noise)
|
|
257
|
+
const unusedComponentCount = metrics.unusedComponentCount ?? 0;
|
|
258
|
+
const repeatedPatternCount = metrics.repeatedPatternCount ?? 0;
|
|
259
|
+
if (unusedComponentCount > 10) {
|
|
260
|
+
if (unusedComponentCount > 50) {
|
|
261
|
+
suggestions.push(`${unusedComponentCount} unused components detected — consider a cleanup sprint to remove dead code`);
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
suggestions.push(`${unusedComponentCount} unused components — review and remove dead code`);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (repeatedPatternCount > 5) {
|
|
268
|
+
if (repeatedPatternCount > 20) {
|
|
269
|
+
suggestions.push(`${repeatedPatternCount} repeated patterns — significant duplication, extract to a shared component library`);
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
suggestions.push(`${repeatedPatternCount} repeated patterns — extract to shared components`);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
// Pillar 2: Token Health (0-20)
|
|
276
|
+
// Four sub-factors, each 0-5 points:
|
|
277
|
+
// 1. Utility framework (0-5): Has Tailwind, CSS-in-JS, etc.
|
|
278
|
+
// 2. Design system library (0-5): Has MUI, Chakra, Mantine, etc.
|
|
279
|
+
// 3. Token definition coverage (0-5): Has tokens across categories
|
|
280
|
+
// 4. Token usage ratio (0-5): What fraction of defined tokens are used
|
|
281
|
+
// Sub-factor 1: Utility framework (0-5)
|
|
282
|
+
// Scaled: 3 for having the framework, +2 when tokens are also defined
|
|
283
|
+
const utilityPoints = metrics.hasUtilityFramework
|
|
284
|
+
? (metrics.tokenCount > 0 ? 5 : 3)
|
|
285
|
+
: 0;
|
|
286
|
+
// Sub-factor 2: Design system library (0-5)
|
|
287
|
+
// Scaled: 3 for having the library, +2 when tokens are also defined
|
|
288
|
+
const libraryPoints = metrics.hasDesignSystemLibrary
|
|
289
|
+
? (metrics.tokenCount > 0 ? 5 : 3)
|
|
290
|
+
: 0;
|
|
291
|
+
// Sub-factor 3: Token definition coverage (0-5)
|
|
292
|
+
// More tokens = more structured. Cap at 20 tokens for full credit.
|
|
293
|
+
// Continuous (not rounded) for granular scoring.
|
|
294
|
+
const tokenCoveragePoints = metrics.tokenCount > 0
|
|
295
|
+
? 5 * clamp(metrics.tokenCount / 20, 0, 1)
|
|
296
|
+
: 0;
|
|
297
|
+
// Sub-factor 4: Token usage ratio (0-5)
|
|
298
|
+
// What fraction of tokens are actually used? All used = 5, all unused = 0.
|
|
299
|
+
let tokenUsagePoints;
|
|
300
|
+
if (metrics.tokenCount > 0) {
|
|
301
|
+
const usedTokens = metrics.tokenCount - metrics.unusedTokenCount;
|
|
302
|
+
tokenUsagePoints = 5 * clamp(usedTokens / metrics.tokenCount, 0, 1);
|
|
303
|
+
}
|
|
304
|
+
else if (metrics.hasUtilityFramework || metrics.hasDesignSystemLibrary) {
|
|
305
|
+
// No explicit tokens but has a framework/library — give partial credit
|
|
306
|
+
// because the framework handles tokenization internally
|
|
307
|
+
tokenUsagePoints = density < 0.5 ? 5 : density < 1.0 ? 3 : 1;
|
|
308
|
+
}
|
|
309
|
+
else if (density < 0.1) {
|
|
310
|
+
// No explicit system but very few hardcoded values = implied system
|
|
311
|
+
tokenUsagePoints = 3;
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
tokenUsagePoints = 0;
|
|
315
|
+
}
|
|
316
|
+
// Round the combined score for integer totals
|
|
317
|
+
const tokenHealthScore = Math.round(utilityPoints + libraryPoints + tokenCoveragePoints + tokenUsagePoints);
|
|
318
|
+
// Token health suggestions (threshold-based)
|
|
319
|
+
if (metrics.tokenCount > 0 && metrics.unusedTokenCount > 5) {
|
|
320
|
+
const unusedPct = Math.round((metrics.unusedTokenCount / metrics.tokenCount) * 100);
|
|
321
|
+
if (unusedPct > 50) {
|
|
322
|
+
suggestions.push(`${metrics.unusedTokenCount} of ${metrics.tokenCount} tokens unused (${unusedPct}%) — many tokens may be stale, audit your token definitions`);
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
suggestions.push(`${metrics.unusedTokenCount} tokens defined but unused — wire them into components or remove stale definitions`);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
if (tokenHealthScore < 10 && metrics.componentCount > 0) {
|
|
329
|
+
if (frameworks.includes('tailwind')) {
|
|
330
|
+
suggestions.push('Tailwind detected but token coverage is low — extend your theme config with custom values');
|
|
331
|
+
}
|
|
332
|
+
else if (frameworks.includes('mui') || frameworks.includes('chakra') || frameworks.includes('mantine')) {
|
|
333
|
+
const lib = frameworks.find(f => ['mui', 'chakra', 'mantine'].includes(f)) ?? 'your library';
|
|
334
|
+
suggestions.push(`${lib} detected — use its theming API for consistent values across components`);
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
suggestions.push('No design token system detected — add CSS custom properties or a utility framework like Tailwind');
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
// Pillar 3: Consistency (0-10)
|
|
341
|
+
// Includes naming-inconsistency + semantic-mismatch signals
|
|
342
|
+
const inconsistencyCount = metrics.namingInconsistencyCount + (metrics.semanticMismatchCount ?? 0);
|
|
343
|
+
const namingRate = inconsistencyCount / Math.max(metrics.componentCount, 1);
|
|
344
|
+
const consistencyRaw = 10 * clamp(1 - namingRate / 0.25, 0, 1);
|
|
345
|
+
// Only surface naming inconsistencies above noise threshold
|
|
346
|
+
if (inconsistencyCount > 3 || (inconsistencyCount > 0 && namingRate > 0.05)) {
|
|
347
|
+
if (namingRate > 0.15) {
|
|
348
|
+
suggestions.push(`${inconsistencyCount} naming/semantic inconsistencies across ${metrics.componentCount} components — establish and document naming conventions`);
|
|
349
|
+
}
|
|
350
|
+
else if (namingRate > 0.05) {
|
|
351
|
+
suggestions.push(`${inconsistencyCount} naming inconsistencies — standardize prop/component conventions`);
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
suggestions.push(`${inconsistencyCount} minor naming inconsistencies — consider standardizing conventions`);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
// Pillar 4: Critical Issues (0-10)
|
|
358
|
+
// Includes critical severity + deprecated patterns (2 deprecated = 1 critical equivalent)
|
|
359
|
+
const deprecatedCount = metrics.deprecatedPatternCount ?? 0;
|
|
360
|
+
const highDensityFiles = metrics.highDensityFileCount ?? 0;
|
|
361
|
+
const effectiveCriticalCount = metrics.criticalCount
|
|
362
|
+
+ Math.ceil(deprecatedCount / 2)
|
|
363
|
+
+ Math.floor(highDensityFiles / 3);
|
|
364
|
+
// Use 2-point steps for finer granularity (was 3-point)
|
|
365
|
+
const criticalRaw = Math.max(0, 10 - effectiveCriticalCount * 2);
|
|
366
|
+
if (metrics.criticalCount > 0) {
|
|
367
|
+
suggestions.push(`${metrics.criticalCount} critical issue${metrics.criticalCount === 1 ? '' : 's'} (accessibility/contrast) — fix immediately`);
|
|
368
|
+
}
|
|
369
|
+
if (deprecatedCount > 0) {
|
|
370
|
+
suggestions.push(`${deprecatedCount} deprecated pattern${deprecatedCount === 1 ? '' : 's'} — migrate to current API`);
|
|
371
|
+
}
|
|
372
|
+
if (metrics.uniqueSpacingValues && metrics.uniqueSpacingValues > 15) {
|
|
373
|
+
suggestions.push(`${metrics.uniqueSpacingValues} unique spacing values — consolidate to a consistent spacing scale (e.g., 4px/8px grid)`);
|
|
374
|
+
}
|
|
375
|
+
// Scale consistency and criticalIssues for repos with very few components
|
|
376
|
+
// Prevents trivially maxing these pillars when there's nothing to evaluate
|
|
377
|
+
const componentScale = Math.min(metrics.componentCount / 3, 1);
|
|
378
|
+
// Use raw (unrounded) values for total calculation to produce more distinct scores
|
|
379
|
+
const scaledConsistencyRaw = consistencyRaw * componentScale;
|
|
380
|
+
const scaledCriticalRaw = criticalRaw * componentScale;
|
|
381
|
+
const scaledConsistencyScore = Math.round(scaledConsistencyRaw);
|
|
382
|
+
const scaledCriticalScore = Math.round(scaledCriticalRaw);
|
|
383
|
+
// Total uses raw values, rounded once at the end for maximum granularity
|
|
384
|
+
let total = Math.round(valueDisciplineRaw + tokenHealthScore + scaledConsistencyRaw + scaledCriticalRaw);
|
|
385
|
+
// Drift density penalty: prevent high-drift repos from scoring "Great"
|
|
386
|
+
// Apps with many drift signals relative to their size should be capped
|
|
387
|
+
const totalDrift = metrics.totalDriftCount ?? metrics.hardcodedValueCount;
|
|
388
|
+
if (totalDrift > 0 && metrics.componentCount > 0) {
|
|
389
|
+
const driftPerComponent = totalDrift / metrics.componentCount;
|
|
390
|
+
if (totalDrift > 200) {
|
|
391
|
+
// >200 drift signals: cap at OK (69)
|
|
392
|
+
total = Math.min(total, 69);
|
|
393
|
+
}
|
|
394
|
+
else if (totalDrift > 100) {
|
|
395
|
+
// >100 drift: graduated cap based on density (74-84)
|
|
396
|
+
const densityFactor = clamp(driftPerComponent, 0, 1);
|
|
397
|
+
const cap = Math.round(74 + (1 - densityFactor) * 10);
|
|
398
|
+
total = Math.min(total, cap);
|
|
399
|
+
}
|
|
400
|
+
else if (totalDrift > 50 && driftPerComponent > 0.3) {
|
|
401
|
+
// >50 drift + high density: cap at 89
|
|
402
|
+
total = Math.min(total, 89);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
// Ensure every scored app gets at least 1 suggestion
|
|
406
|
+
if (suggestions.length === 0) {
|
|
407
|
+
if (total === 100) {
|
|
408
|
+
suggestions.push('Perfect design system health — no issues detected');
|
|
409
|
+
}
|
|
410
|
+
else if (total >= 90) {
|
|
411
|
+
// Find the weakest pillar for aspirational suggestions
|
|
412
|
+
const vdPct = valueDisciplineScore / 60;
|
|
413
|
+
const thPct = tokenHealthScore / 20;
|
|
414
|
+
const coPct = scaledConsistencyScore / 10;
|
|
415
|
+
const ciPct = scaledCriticalScore / 10;
|
|
416
|
+
const minPct = Math.min(vdPct, thPct, coPct, ciPct);
|
|
417
|
+
if (minPct === vdPct && valueDisciplineScore < 60) {
|
|
418
|
+
suggestions.push(`Score ${total} — to reach 100, reduce the remaining hardcoded values in your components`);
|
|
419
|
+
}
|
|
420
|
+
else if (minPct === thPct && tokenHealthScore < 20) {
|
|
421
|
+
suggestions.push(`Score ${total} — to reach 100, improve token coverage and usage`);
|
|
422
|
+
}
|
|
423
|
+
else if (minPct === coPct && scaledConsistencyScore < 10) {
|
|
424
|
+
suggestions.push(`Score ${total} — to reach 100, address remaining naming inconsistencies`);
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
427
|
+
suggestions.push(`Score ${total} — nearly perfect, review remaining drift signals for final improvements`);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
else {
|
|
431
|
+
// 80-89: point to the weakest pillar
|
|
432
|
+
const vdPct = valueDisciplineScore / 60;
|
|
433
|
+
const thPct = tokenHealthScore / 20;
|
|
434
|
+
const coPct = metrics.componentCount >= 3 ? scaledConsistencyScore / 10 : 1;
|
|
435
|
+
const ciPct = metrics.componentCount >= 3 ? scaledCriticalScore / 10 : 1;
|
|
436
|
+
const minPct = Math.min(vdPct, thPct, coPct, ciPct);
|
|
437
|
+
if (minPct === vdPct) {
|
|
438
|
+
suggestions.push(`Your weakest area is Value Discipline (${valueDisciplineScore}/60) — focus on reducing hardcoded values`);
|
|
439
|
+
}
|
|
440
|
+
else if (minPct === thPct) {
|
|
441
|
+
suggestions.push(`Your weakest area is Token Health (${tokenHealthScore}/20) — improve token definitions and usage`);
|
|
442
|
+
}
|
|
443
|
+
else if (minPct === coPct) {
|
|
444
|
+
suggestions.push(`Your weakest area is Consistency (${scaledConsistencyScore}/10) — standardize naming conventions`);
|
|
445
|
+
}
|
|
446
|
+
else {
|
|
447
|
+
suggestions.push(`Your weakest area is Critical Issues (${scaledCriticalScore}/10) — address accessibility and deprecated patterns`);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
return {
|
|
452
|
+
score: total,
|
|
453
|
+
tier: getHealthTier(total),
|
|
454
|
+
pillars: {
|
|
455
|
+
valueDiscipline: {
|
|
456
|
+
name: 'Value Discipline',
|
|
457
|
+
score: valueDisciplineScore,
|
|
458
|
+
maxScore: 60,
|
|
459
|
+
description: 'Hardcoded values per component',
|
|
460
|
+
},
|
|
461
|
+
tokenHealth: {
|
|
462
|
+
name: 'Token Health',
|
|
463
|
+
score: tokenHealthScore,
|
|
464
|
+
maxScore: 20,
|
|
465
|
+
description: 'Token system adoption',
|
|
466
|
+
},
|
|
467
|
+
consistency: {
|
|
468
|
+
name: 'Consistency',
|
|
469
|
+
score: scaledConsistencyScore,
|
|
470
|
+
maxScore: 10,
|
|
471
|
+
description: 'Naming convention adherence',
|
|
472
|
+
},
|
|
473
|
+
criticalIssues: {
|
|
474
|
+
name: 'Critical Issues',
|
|
475
|
+
score: scaledCriticalScore,
|
|
476
|
+
maxScore: 10,
|
|
477
|
+
description: 'Accessibility and critical failures',
|
|
478
|
+
},
|
|
479
|
+
},
|
|
480
|
+
suggestions,
|
|
481
|
+
metrics,
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
function clamp(value, min, max) {
|
|
485
|
+
return Math.min(max, Math.max(min, value));
|
|
486
|
+
}
|
|
487
|
+
export function getHealthTier(score) {
|
|
488
|
+
if (score >= 80)
|
|
489
|
+
return 'Great';
|
|
490
|
+
if (score >= 60)
|
|
491
|
+
return 'Good';
|
|
492
|
+
if (score >= 40)
|
|
493
|
+
return 'OK';
|
|
494
|
+
if (score >= 20)
|
|
495
|
+
return 'Bad';
|
|
496
|
+
return 'Terrible';
|
|
497
|
+
}
|
|
498
|
+
//# sourceMappingURL=audit.js.map
|