@buoy-design/core 0.1.7 → 0.1.9
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/analyzers/accessibility-analyzer.d.ts +22 -0
- package/dist/analysis/analyzers/accessibility-analyzer.d.ts.map +1 -0
- package/dist/analysis/analyzers/accessibility-analyzer.js +127 -0
- package/dist/analysis/analyzers/accessibility-analyzer.js.map +1 -0
- package/dist/analysis/analyzers/duplicate-detector.d.ts +44 -0
- package/dist/analysis/analyzers/duplicate-detector.d.ts.map +1 -0
- package/dist/analysis/analyzers/duplicate-detector.js +137 -0
- package/dist/analysis/analyzers/duplicate-detector.js.map +1 -0
- package/dist/analysis/analyzers/index.d.ts +5 -0
- package/dist/analysis/analyzers/index.d.ts.map +1 -0
- package/dist/analysis/analyzers/index.js +5 -0
- package/dist/analysis/analyzers/index.js.map +1 -0
- package/dist/analysis/analyzers/naming-analyzer.d.ts +43 -0
- package/dist/analysis/analyzers/naming-analyzer.d.ts.map +1 -0
- package/dist/analysis/analyzers/naming-analyzer.js +70 -0
- package/dist/analysis/analyzers/naming-analyzer.js.map +1 -0
- package/dist/analysis/analyzers/prop-analyzer.d.ts +60 -0
- package/dist/analysis/analyzers/prop-analyzer.d.ts.map +1 -0
- package/dist/analysis/analyzers/prop-analyzer.js +130 -0
- package/dist/analysis/analyzers/prop-analyzer.js.map +1 -0
- package/dist/analysis/audit.d.ts +10 -0
- package/dist/analysis/audit.d.ts.map +1 -1
- package/dist/analysis/audit.js +22 -15
- package/dist/analysis/audit.js.map +1 -1
- package/dist/analysis/semantic-diff.d.ts +20 -68
- package/dist/analysis/semantic-diff.d.ts.map +1 -1
- package/dist/analysis/semantic-diff.js +213 -767
- package/dist/analysis/semantic-diff.js.map +1 -1
- package/dist/analysis/utils/color-contrast.d.ts +52 -0
- package/dist/analysis/utils/color-contrast.d.ts.map +1 -0
- package/dist/analysis/utils/color-contrast.js +109 -0
- package/dist/analysis/utils/color-contrast.js.map +1 -0
- package/dist/analysis/utils/index.d.ts +2 -0
- package/dist/analysis/utils/index.d.ts.map +1 -0
- package/dist/analysis/utils/index.js +2 -0
- package/dist/analysis/utils/index.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Accessibility Analyzer
|
|
3
|
+
*
|
|
4
|
+
* Checks components for accessibility issues including:
|
|
5
|
+
* - Missing ARIA labels on interactive elements
|
|
6
|
+
* - Color contrast violations (WCAG AA/AAA)
|
|
7
|
+
*/
|
|
8
|
+
import type { Component, DriftSignal } from "../../models/index.js";
|
|
9
|
+
/**
|
|
10
|
+
* Check if a component is interactive based on its name
|
|
11
|
+
*/
|
|
12
|
+
export declare function isInteractiveComponent(componentName: string): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Check component for general accessibility issues
|
|
15
|
+
*/
|
|
16
|
+
export declare function checkAccessibility(component: Component): string[];
|
|
17
|
+
/**
|
|
18
|
+
* Check for color contrast issues in a component.
|
|
19
|
+
* Detects foreground/background color pairs that fail WCAG contrast ratios.
|
|
20
|
+
*/
|
|
21
|
+
export declare function checkColorContrast(component: Component): DriftSignal[];
|
|
22
|
+
//# sourceMappingURL=accessibility-analyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accessibility-analyzer.d.ts","sourceRoot":"","sources":["../../../src/analysis/analyzers/accessibility-analyzer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAe,MAAM,uBAAuB,CAAC;AAoBjF;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAIrE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,SAAS,GAAG,MAAM,EAAE,CAyBjE;AAyBD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,SAAS,GAAG,WAAW,EAAE,CAiEtE"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Accessibility Analyzer
|
|
3
|
+
*
|
|
4
|
+
* Checks components for accessibility issues including:
|
|
5
|
+
* - Missing ARIA labels on interactive elements
|
|
6
|
+
* - Color contrast violations (WCAG AA/AAA)
|
|
7
|
+
*/
|
|
8
|
+
import { createDriftId } from "../../models/index.js";
|
|
9
|
+
import { colorToRgb, getContrastRatio, WCAG_THRESHOLDS, } from "../utils/color-contrast.js";
|
|
10
|
+
/**
|
|
11
|
+
* Interactive component types that require accessibility considerations
|
|
12
|
+
*/
|
|
13
|
+
const INTERACTIVE_COMPONENTS = [
|
|
14
|
+
"Button",
|
|
15
|
+
"Link",
|
|
16
|
+
"Input",
|
|
17
|
+
"Select",
|
|
18
|
+
"Checkbox",
|
|
19
|
+
"Radio",
|
|
20
|
+
];
|
|
21
|
+
/**
|
|
22
|
+
* Check if a component is interactive based on its name
|
|
23
|
+
*/
|
|
24
|
+
export function isInteractiveComponent(componentName) {
|
|
25
|
+
return INTERACTIVE_COMPONENTS.some((ic) => componentName.toLowerCase().includes(ic.toLowerCase()));
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Check component for general accessibility issues
|
|
29
|
+
*/
|
|
30
|
+
export function checkAccessibility(component) {
|
|
31
|
+
const issues = [];
|
|
32
|
+
const isInteractive = isInteractiveComponent(component.name);
|
|
33
|
+
if (isInteractive) {
|
|
34
|
+
const hasAriaLabel = component.props.some((p) => p.name.toLowerCase().includes("arialabel") ||
|
|
35
|
+
p.name.toLowerCase().includes("aria-label"));
|
|
36
|
+
const hasChildren = component.props.some((p) => p.name.toLowerCase() === "children");
|
|
37
|
+
if (!hasAriaLabel &&
|
|
38
|
+
!hasChildren &&
|
|
39
|
+
component.metadata.accessibility?.issues) {
|
|
40
|
+
issues.push(...component.metadata.accessibility.issues);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return issues;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Convert a component to a DriftSource for signal generation
|
|
47
|
+
*/
|
|
48
|
+
function componentToDriftSource(comp) {
|
|
49
|
+
let location = "";
|
|
50
|
+
if (comp.source.type === "figma") {
|
|
51
|
+
location = comp.source.url || comp.source.nodeId;
|
|
52
|
+
}
|
|
53
|
+
else if (comp.source.type === "storybook") {
|
|
54
|
+
location = comp.source.url || comp.source.storyId;
|
|
55
|
+
}
|
|
56
|
+
else if (comp.source.path) {
|
|
57
|
+
location = comp.source.line
|
|
58
|
+
? `${comp.source.path}:${comp.source.line}`
|
|
59
|
+
: comp.source.path;
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
entityType: "component",
|
|
63
|
+
entityId: comp.id,
|
|
64
|
+
entityName: comp.name,
|
|
65
|
+
location,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Check for color contrast issues in a component.
|
|
70
|
+
* Detects foreground/background color pairs that fail WCAG contrast ratios.
|
|
71
|
+
*/
|
|
72
|
+
export function checkColorContrast(component) {
|
|
73
|
+
const drifts = [];
|
|
74
|
+
if (!component.metadata.hardcodedValues) {
|
|
75
|
+
return drifts;
|
|
76
|
+
}
|
|
77
|
+
const colorValues = component.metadata.hardcodedValues.filter((h) => h.type === "color");
|
|
78
|
+
// Group colors by property
|
|
79
|
+
const colorsByProperty = new Map();
|
|
80
|
+
for (const cv of colorValues) {
|
|
81
|
+
const prop = cv.property.toLowerCase();
|
|
82
|
+
if (!colorsByProperty.has(prop)) {
|
|
83
|
+
colorsByProperty.set(prop, []);
|
|
84
|
+
}
|
|
85
|
+
colorsByProperty.get(prop).push(cv);
|
|
86
|
+
}
|
|
87
|
+
const foregroundColors = colorsByProperty.get("color") || [];
|
|
88
|
+
const backgroundColors = colorsByProperty.get("background-color") ||
|
|
89
|
+
colorsByProperty.get("background") ||
|
|
90
|
+
[];
|
|
91
|
+
// Check contrast ratio for each foreground/background pair
|
|
92
|
+
for (const fg of foregroundColors) {
|
|
93
|
+
for (const bg of backgroundColors) {
|
|
94
|
+
const fgRgb = colorToRgb(fg.value);
|
|
95
|
+
const bgRgb = colorToRgb(bg.value);
|
|
96
|
+
if (!fgRgb || !bgRgb)
|
|
97
|
+
continue;
|
|
98
|
+
const ratio = getContrastRatio(fgRgb, bgRgb);
|
|
99
|
+
if (ratio < WCAG_THRESHOLDS.AA_NORMAL_TEXT) {
|
|
100
|
+
const level = ratio < WCAG_THRESHOLDS.AA_LARGE_TEXT ? "WCAG AA and AAA" : "WCAG AAA";
|
|
101
|
+
drifts.push({
|
|
102
|
+
id: createDriftId("color-contrast", component.id, `${fg.value}-${bg.value}`),
|
|
103
|
+
type: "color-contrast",
|
|
104
|
+
severity: "critical",
|
|
105
|
+
source: componentToDriftSource(component),
|
|
106
|
+
message: `Component "${component.name}" has insufficient color contrast: ${fg.value} on ${bg.value} (ratio: ${ratio.toFixed(2)}:1)`,
|
|
107
|
+
details: {
|
|
108
|
+
expected: `Minimum ${WCAG_THRESHOLDS.AA_NORMAL_TEXT}:1 for WCAG AA, ${WCAG_THRESHOLDS.AAA_NORMAL_TEXT}:1 for AAA`,
|
|
109
|
+
actual: `${ratio.toFixed(2)}:1`,
|
|
110
|
+
suggestions: [
|
|
111
|
+
`Fails ${level} - adjust colors to meet minimum ${WCAG_THRESHOLDS.AA_NORMAL_TEXT}:1 ratio`,
|
|
112
|
+
"Use a contrast checker tool to find accessible color combinations",
|
|
113
|
+
"Consider using design system color tokens with built-in contrast ratios",
|
|
114
|
+
],
|
|
115
|
+
affectedFiles: [
|
|
116
|
+
`${fg.location}: ${fg.property}: ${fg.value}`,
|
|
117
|
+
`${bg.location}: ${bg.property}: ${bg.value}`,
|
|
118
|
+
],
|
|
119
|
+
},
|
|
120
|
+
detectedAt: new Date(),
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return drifts;
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=accessibility-analyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accessibility-analyzer.js","sourceRoot":"","sources":["../../../src/analysis/analyzers/accessibility-analyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Duplicate Component Detector
|
|
3
|
+
*
|
|
4
|
+
* Detects potential duplicate components based on similar names.
|
|
5
|
+
* Only flags true duplicates like Button vs ButtonNew or Card vs CardLegacy.
|
|
6
|
+
* Does NOT flag compound components like Button vs ButtonGroup.
|
|
7
|
+
*/
|
|
8
|
+
import type { Component } from "../../models/index.js";
|
|
9
|
+
/**
|
|
10
|
+
* Semantic suffixes that represent legitimate separate components.
|
|
11
|
+
* Components with these suffixes are NOT duplicates of their base component.
|
|
12
|
+
* e.g., Button vs ButtonGroup are distinct components, not duplicates.
|
|
13
|
+
*/
|
|
14
|
+
export declare const SEMANTIC_COMPONENT_SUFFIXES: readonly ["group", "list", "item", "items", "container", "wrapper", "provider", "context", "header", "footer", "body", "content", "section", "sidebar", "panel", "trigger", "target", "overlay", "portal", "root", "slot", "action", "actions", "icon", "label", "text", "title", "description", "separator", "divider", "small", "large", "mini", "skeleton", "placeholder", "loading", "error", "empty", "input", "field", "control", "message", "helper", "hint", "link", "menu", "submenu", "tab", "tabs", "cell", "row", "column", "columns", "head", "view"];
|
|
15
|
+
/**
|
|
16
|
+
* Version/status suffixes that indicate potential duplicates.
|
|
17
|
+
* Components with ONLY these suffixes are likely duplicates.
|
|
18
|
+
* e.g., Button vs ButtonNew, Card vs CardLegacy
|
|
19
|
+
*/
|
|
20
|
+
export declare const VERSION_SUFFIXES_PATTERN: RegExp;
|
|
21
|
+
/**
|
|
22
|
+
* Result of base name extraction
|
|
23
|
+
*/
|
|
24
|
+
export interface BaseNameResult {
|
|
25
|
+
baseName: string;
|
|
26
|
+
hasVersionSuffix: boolean;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* A group of potentially duplicate components
|
|
30
|
+
*/
|
|
31
|
+
export interface DuplicateGroup {
|
|
32
|
+
components: Component[];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Extract the base name from a component name, stripping version suffixes.
|
|
36
|
+
* Returns whether the name had a version suffix (indicating potential duplicate).
|
|
37
|
+
*/
|
|
38
|
+
export declare function extractBaseName(name: string): BaseNameResult;
|
|
39
|
+
/**
|
|
40
|
+
* Detect potential duplicate components in a list.
|
|
41
|
+
* Only returns groups where at least one component has a version suffix.
|
|
42
|
+
*/
|
|
43
|
+
export declare function detectPotentialDuplicates(components: Component[]): DuplicateGroup[];
|
|
44
|
+
//# sourceMappingURL=duplicate-detector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"duplicate-detector.d.ts","sourceRoot":"","sources":["../../../src/analysis/analyzers/duplicate-detector.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAEvD;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,oiBA+D9B,CAAC;AAEX;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,QACmH,CAAC;AAEzJ;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,SAAS,EAAE,CAAC;CACzB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAwB5D;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,cAAc,EAAE,CA+BnF"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Duplicate Component Detector
|
|
3
|
+
*
|
|
4
|
+
* Detects potential duplicate components based on similar names.
|
|
5
|
+
* Only flags true duplicates like Button vs ButtonNew or Card vs CardLegacy.
|
|
6
|
+
* Does NOT flag compound components like Button vs ButtonGroup.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Semantic suffixes that represent legitimate separate components.
|
|
10
|
+
* Components with these suffixes are NOT duplicates of their base component.
|
|
11
|
+
* e.g., Button vs ButtonGroup are distinct components, not duplicates.
|
|
12
|
+
*/
|
|
13
|
+
export const SEMANTIC_COMPONENT_SUFFIXES = [
|
|
14
|
+
// Compound component patterns
|
|
15
|
+
"group",
|
|
16
|
+
"list",
|
|
17
|
+
"item",
|
|
18
|
+
"items",
|
|
19
|
+
"container",
|
|
20
|
+
"wrapper",
|
|
21
|
+
"provider",
|
|
22
|
+
"context",
|
|
23
|
+
// Layout parts
|
|
24
|
+
"header",
|
|
25
|
+
"footer",
|
|
26
|
+
"body",
|
|
27
|
+
"content",
|
|
28
|
+
"section",
|
|
29
|
+
"sidebar",
|
|
30
|
+
"panel",
|
|
31
|
+
// Specific UI patterns
|
|
32
|
+
"trigger",
|
|
33
|
+
"target",
|
|
34
|
+
"overlay",
|
|
35
|
+
"portal",
|
|
36
|
+
"root",
|
|
37
|
+
"slot",
|
|
38
|
+
"action",
|
|
39
|
+
"actions",
|
|
40
|
+
"icon",
|
|
41
|
+
"label",
|
|
42
|
+
"text",
|
|
43
|
+
"title",
|
|
44
|
+
"description",
|
|
45
|
+
"separator",
|
|
46
|
+
"divider",
|
|
47
|
+
// Size/state variants that are distinct components
|
|
48
|
+
"small",
|
|
49
|
+
"large",
|
|
50
|
+
"mini",
|
|
51
|
+
"skeleton",
|
|
52
|
+
"placeholder",
|
|
53
|
+
"loading",
|
|
54
|
+
"error",
|
|
55
|
+
"empty",
|
|
56
|
+
// Form-related
|
|
57
|
+
"input",
|
|
58
|
+
"field",
|
|
59
|
+
"control",
|
|
60
|
+
"message",
|
|
61
|
+
"helper",
|
|
62
|
+
"hint",
|
|
63
|
+
// Navigation
|
|
64
|
+
"link",
|
|
65
|
+
"menu",
|
|
66
|
+
"submenu",
|
|
67
|
+
"tab",
|
|
68
|
+
"tabs",
|
|
69
|
+
// Data display
|
|
70
|
+
"cell",
|
|
71
|
+
"row",
|
|
72
|
+
"column",
|
|
73
|
+
"columns",
|
|
74
|
+
"head",
|
|
75
|
+
"view",
|
|
76
|
+
];
|
|
77
|
+
/**
|
|
78
|
+
* Version/status suffixes that indicate potential duplicates.
|
|
79
|
+
* Components with ONLY these suffixes are likely duplicates.
|
|
80
|
+
* e.g., Button vs ButtonNew, Card vs CardLegacy
|
|
81
|
+
*/
|
|
82
|
+
export const VERSION_SUFFIXES_PATTERN = /(New|Old|V\d+|Legacy|Updated|Deprecated|Beta|Alpha|Experimental|Next|Previous|Original|Backup|Copy|Clone|Alt|Alternative|Temp|Temporary|WIP|Draft)$/i;
|
|
83
|
+
/**
|
|
84
|
+
* Extract the base name from a component name, stripping version suffixes.
|
|
85
|
+
* Returns whether the name had a version suffix (indicating potential duplicate).
|
|
86
|
+
*/
|
|
87
|
+
export function extractBaseName(name) {
|
|
88
|
+
const lowerName = name.toLowerCase();
|
|
89
|
+
// Check if the name ends with a semantic suffix (legitimate separate component)
|
|
90
|
+
for (const suffix of SEMANTIC_COMPONENT_SUFFIXES) {
|
|
91
|
+
if (lowerName.endsWith(suffix) &&
|
|
92
|
+
lowerName.length > suffix.length &&
|
|
93
|
+
// Ensure the suffix is at a word boundary (e.g., "ButtonGroup" not "Buttong")
|
|
94
|
+
lowerName[lowerName.length - suffix.length - 1]?.match(/[a-z0-9]/)) {
|
|
95
|
+
// This is a compound component, not a duplicate candidate
|
|
96
|
+
return { baseName: lowerName, hasVersionSuffix: false };
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Check for version suffixes that indicate duplicates
|
|
100
|
+
const hasVersionSuffix = VERSION_SUFFIXES_PATTERN.test(name);
|
|
101
|
+
const strippedName = name
|
|
102
|
+
.replace(VERSION_SUFFIXES_PATTERN, "")
|
|
103
|
+
.replace(/\d+$/, "") // Strip trailing numbers
|
|
104
|
+
.toLowerCase();
|
|
105
|
+
return { baseName: strippedName, hasVersionSuffix };
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Detect potential duplicate components in a list.
|
|
109
|
+
* Only returns groups where at least one component has a version suffix.
|
|
110
|
+
*/
|
|
111
|
+
export function detectPotentialDuplicates(components) {
|
|
112
|
+
const duplicates = [];
|
|
113
|
+
const processed = new Set();
|
|
114
|
+
for (const comp of components) {
|
|
115
|
+
if (processed.has(comp.id))
|
|
116
|
+
continue;
|
|
117
|
+
const { baseName, hasVersionSuffix } = extractBaseName(comp.name);
|
|
118
|
+
const similar = components.filter((c) => {
|
|
119
|
+
if (c.id === comp.id)
|
|
120
|
+
return false;
|
|
121
|
+
const other = extractBaseName(c.name);
|
|
122
|
+
// Only match if base names are identical
|
|
123
|
+
if (baseName !== other.baseName || baseName.length < 3) {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
// At least one component must have a version suffix to be a duplicate
|
|
127
|
+
return hasVersionSuffix || other.hasVersionSuffix;
|
|
128
|
+
});
|
|
129
|
+
if (similar.length > 0) {
|
|
130
|
+
const group = [comp, ...similar];
|
|
131
|
+
group.forEach((c) => processed.add(c.id));
|
|
132
|
+
duplicates.push({ components: group });
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return duplicates;
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=duplicate-detector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"duplicate-detector.js","sourceRoot":"","sources":["../../../src/analysis/analyzers/duplicate-detector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/analysis/analyzers/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,oBAAoB,CAAC;AACnC,cAAc,6BAA6B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/analysis/analyzers/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,oBAAoB,CAAC;AACnC,cAAc,6BAA6B,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Naming Pattern Analyzer
|
|
3
|
+
*
|
|
4
|
+
* Detects and validates naming conventions across components.
|
|
5
|
+
* Identifies inconsistencies based on the project's own patterns,
|
|
6
|
+
* not arbitrary external rules.
|
|
7
|
+
*/
|
|
8
|
+
import type { Component } from "../../models/index.js";
|
|
9
|
+
/**
|
|
10
|
+
* Analysis of naming patterns found in the codebase
|
|
11
|
+
*/
|
|
12
|
+
export interface NamingPatternAnalysis {
|
|
13
|
+
patterns: {
|
|
14
|
+
PascalCase: number;
|
|
15
|
+
camelCase: number;
|
|
16
|
+
"kebab-case": number;
|
|
17
|
+
snake_case: number;
|
|
18
|
+
other: number;
|
|
19
|
+
};
|
|
20
|
+
dominant: NamingPattern | null;
|
|
21
|
+
total: number;
|
|
22
|
+
}
|
|
23
|
+
export type NamingPattern = "PascalCase" | "camelCase" | "kebab-case" | "snake_case" | "other";
|
|
24
|
+
/**
|
|
25
|
+
* Result of a naming consistency check
|
|
26
|
+
*/
|
|
27
|
+
export interface NamingIssue {
|
|
28
|
+
message: string;
|
|
29
|
+
suggestion: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Identify the naming pattern of a string
|
|
33
|
+
*/
|
|
34
|
+
export declare function identifyNamingPattern(name: string): NamingPattern;
|
|
35
|
+
/**
|
|
36
|
+
* Detect the dominant naming patterns in a set of components
|
|
37
|
+
*/
|
|
38
|
+
export declare function detectNamingPatterns(components: Component[]): NamingPatternAnalysis;
|
|
39
|
+
/**
|
|
40
|
+
* Check if a component name is consistent with the project's naming patterns
|
|
41
|
+
*/
|
|
42
|
+
export declare function checkNamingConsistency(name: string, patterns: NamingPatternAnalysis): NamingIssue | null;
|
|
43
|
+
//# sourceMappingURL=naming-analyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"naming-analyzer.d.ts","sourceRoot":"","sources":["../../../src/analysis/analyzers/naming-analyzer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAGvD;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE;QACR,UAAU,EAAE,MAAM,CAAC;QACnB,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC;QACnB,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;IAC/B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,WAAW,GAAG,YAAY,GAAG,YAAY,GAAG,OAAO,CAAC;AAE/F;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAMjE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,qBAAqB,CA8BnF;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,qBAAqB,GAC9B,WAAW,GAAG,IAAI,CAkBpB"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Naming Pattern Analyzer
|
|
3
|
+
*
|
|
4
|
+
* Detects and validates naming conventions across components.
|
|
5
|
+
* Identifies inconsistencies based on the project's own patterns,
|
|
6
|
+
* not arbitrary external rules.
|
|
7
|
+
*/
|
|
8
|
+
import { NAMING_CONFIG, getOutlierThreshold } from "../config.js";
|
|
9
|
+
/**
|
|
10
|
+
* Identify the naming pattern of a string
|
|
11
|
+
*/
|
|
12
|
+
export function identifyNamingPattern(name) {
|
|
13
|
+
if (/^[A-Z][a-zA-Z0-9]*$/.test(name))
|
|
14
|
+
return "PascalCase";
|
|
15
|
+
if (/^[a-z][a-zA-Z0-9]*$/.test(name))
|
|
16
|
+
return "camelCase";
|
|
17
|
+
if (/^[a-z][a-z0-9-]*$/.test(name))
|
|
18
|
+
return "kebab-case";
|
|
19
|
+
if (/^[a-z][a-z0-9_]*$/.test(name))
|
|
20
|
+
return "snake_case";
|
|
21
|
+
return "other";
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Detect the dominant naming patterns in a set of components
|
|
25
|
+
*/
|
|
26
|
+
export function detectNamingPatterns(components) {
|
|
27
|
+
const patterns = {
|
|
28
|
+
PascalCase: 0,
|
|
29
|
+
camelCase: 0,
|
|
30
|
+
"kebab-case": 0,
|
|
31
|
+
snake_case: 0,
|
|
32
|
+
other: 0,
|
|
33
|
+
};
|
|
34
|
+
for (const comp of components) {
|
|
35
|
+
const pattern = identifyNamingPattern(comp.name);
|
|
36
|
+
patterns[pattern]++;
|
|
37
|
+
}
|
|
38
|
+
// Find dominant pattern (must exceed threshold to be considered dominant)
|
|
39
|
+
const total = components.length;
|
|
40
|
+
let dominant = null;
|
|
41
|
+
let dominantCount = 0;
|
|
42
|
+
for (const [pattern, count] of Object.entries(patterns)) {
|
|
43
|
+
if (count > dominantCount &&
|
|
44
|
+
count / total > NAMING_CONFIG.dominantPatternThreshold) {
|
|
45
|
+
dominant = pattern;
|
|
46
|
+
dominantCount = count;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return { patterns, dominant, total };
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Check if a component name is consistent with the project's naming patterns
|
|
53
|
+
*/
|
|
54
|
+
export function checkNamingConsistency(name, patterns) {
|
|
55
|
+
if (!patterns.dominant)
|
|
56
|
+
return null; // No clear pattern, don't flag
|
|
57
|
+
const thisPattern = identifyNamingPattern(name);
|
|
58
|
+
if (thisPattern === patterns.dominant)
|
|
59
|
+
return null;
|
|
60
|
+
// Only flag if this is a clear outlier
|
|
61
|
+
const outlierThreshold = getOutlierThreshold(patterns.total);
|
|
62
|
+
if (patterns.patterns[patterns.dominant] < outlierThreshold)
|
|
63
|
+
return null;
|
|
64
|
+
const percentage = Math.round((patterns.patterns[patterns.dominant] / patterns.total) * 100);
|
|
65
|
+
return {
|
|
66
|
+
message: `Component "${name}" uses ${thisPattern} but ${percentage}% of components use ${patterns.dominant}`,
|
|
67
|
+
suggestion: `Consider renaming to match project convention (${patterns.dominant})`,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=naming-analyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"naming-analyzer.js","sourceRoot":"","sources":["../../../src/analysis/analyzers/naming-analyzer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AA2BlE;;GAEG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAY;IAChD,IAAI,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,YAAY,CAAC;IAC1D,IAAI,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,WAAW,CAAC;IACzD,IAAI,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,YAAY,CAAC;IACxD,IAAI,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,YAAY,CAAC;IACxD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAAC,UAAuB;IAC1D,MAAM,QAAQ,GAAG;QACf,UAAU,EAAE,CAAC;QACb,SAAS,EAAE,CAAC;QACZ,YAAY,EAAE,CAAC;QACf,UAAU,EAAE,CAAC;QACb,KAAK,EAAE,CAAC;KACT,CAAC;IAEF,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,MAAM,OAAO,GAAG,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjD,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;IACtB,CAAC;IAED,0EAA0E;IAC1E,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC;IAChC,IAAI,QAAQ,GAAyB,IAAI,CAAC;IAC1C,IAAI,aAAa,GAAG,CAAC,CAAC;IAEtB,KAAK,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QACxD,IACE,KAAK,GAAG,aAAa;YACrB,KAAK,GAAG,KAAK,GAAG,aAAa,CAAC,wBAAwB,EACtD,CAAC;YACD,QAAQ,GAAG,OAAwB,CAAC;YACpC,aAAa,GAAG,KAAK,CAAC;QACxB,CAAC;IACH,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;AACvC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,sBAAsB,CACpC,IAAY,EACZ,QAA+B;IAE/B,IAAI,CAAC,QAAQ,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC,CAAC,+BAA+B;IAEpE,MAAM,WAAW,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;IAChD,IAAI,WAAW,KAAK,QAAQ,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAEnD,uCAAuC;IACvC,MAAM,gBAAgB,GAAG,mBAAmB,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC7D,IAAI,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAE,GAAG,gBAAgB;QAAE,OAAO,IAAI,CAAC;IAE1E,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAC3B,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,GAAG,CAC/D,CAAC;IAEF,OAAO;QACL,OAAO,EAAE,cAAc,IAAI,UAAU,WAAW,QAAQ,UAAU,uBAAuB,QAAQ,CAAC,QAAQ,EAAE;QAC5G,UAAU,EAAE,kDAAkD,QAAQ,CAAC,QAAQ,GAAG;KACnF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prop Type Analyzer
|
|
3
|
+
*
|
|
4
|
+
* Analyzes prop type consistency and naming conventions across components.
|
|
5
|
+
* Detects inconsistencies where the same prop name uses different types
|
|
6
|
+
* in different components.
|
|
7
|
+
*/
|
|
8
|
+
import type { Component } from "../../models/index.js";
|
|
9
|
+
/**
|
|
10
|
+
* Usage statistics for a prop type
|
|
11
|
+
*/
|
|
12
|
+
export interface PropTypeUsage {
|
|
13
|
+
types: Map<string, {
|
|
14
|
+
count: number;
|
|
15
|
+
examples: string[];
|
|
16
|
+
}>;
|
|
17
|
+
total: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Result of a prop type conflict check
|
|
21
|
+
*/
|
|
22
|
+
export interface PropTypeConflict {
|
|
23
|
+
dominantType: string;
|
|
24
|
+
examples: string[];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Issue with prop naming consistency
|
|
28
|
+
*/
|
|
29
|
+
export interface PropNamingIssue {
|
|
30
|
+
propName: string;
|
|
31
|
+
message: string;
|
|
32
|
+
suggestion: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Build a map of prop names to their types across all components
|
|
36
|
+
*/
|
|
37
|
+
export declare function buildPropTypeMap(components: Component[]): Map<string, PropTypeUsage>;
|
|
38
|
+
/**
|
|
39
|
+
* Check if a prop's type conflicts with the dominant type for that prop name
|
|
40
|
+
*/
|
|
41
|
+
export declare function checkPropTypeConsistency(prop: {
|
|
42
|
+
name: string;
|
|
43
|
+
type: string;
|
|
44
|
+
}, propTypeMap: Map<string, PropTypeUsage>): PropTypeConflict | null;
|
|
45
|
+
/**
|
|
46
|
+
* Build a map of semantic prop purposes to their naming patterns
|
|
47
|
+
*/
|
|
48
|
+
export declare function buildPropNamingMap(components: Component[]): Map<string, string[]>;
|
|
49
|
+
/**
|
|
50
|
+
* Find the dominant prefix pattern in a list of prop names
|
|
51
|
+
*/
|
|
52
|
+
export declare function findDominantPropPattern(propNames: string[]): {
|
|
53
|
+
prefix: string;
|
|
54
|
+
count: number;
|
|
55
|
+
} | null;
|
|
56
|
+
/**
|
|
57
|
+
* Check prop naming consistency for a component against project patterns
|
|
58
|
+
*/
|
|
59
|
+
export declare function checkPropNamingConsistency(component: Component, propNamingMap: Map<string, string[]>): PropNamingIssue[];
|
|
60
|
+
//# sourceMappingURL=prop-analyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prop-analyzer.d.ts","sourceRoot":"","sources":["../../../src/analysis/analyzers/prop-analyzer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAGvD;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IAC1D,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAwBpF;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EACpC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GACtC,gBAAgB,GAAG,IAAI,CAyBzB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAuBjF;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,MAAM,EAAE,GAClB;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAiB1C;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,SAAS,EACpB,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GACnC,eAAe,EAAE,CA4BnB"}
|