@brad-frost-web/eddie-brain 0.32.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/README.md +109 -0
- package/dist/analyze/drift-detector.d.ts +30 -0
- package/dist/analyze/drift-detector.d.ts.map +1 -0
- package/dist/analyze/drift-detector.js +310 -0
- package/dist/analyze/drift-detector.js.map +1 -0
- package/dist/analyze/health-scorer.d.ts +71 -0
- package/dist/analyze/health-scorer.d.ts.map +1 -0
- package/dist/analyze/health-scorer.js +420 -0
- package/dist/analyze/health-scorer.js.map +1 -0
- package/dist/analyze/index.d.ts +11 -0
- package/dist/analyze/index.d.ts.map +1 -0
- package/dist/analyze/index.js +11 -0
- package/dist/analyze/index.js.map +1 -0
- package/dist/analyze/naming-validator.d.ts +99 -0
- package/dist/analyze/naming-validator.d.ts.map +1 -0
- package/dist/analyze/naming-validator.js +430 -0
- package/dist/analyze/naming-validator.js.map +1 -0
- package/dist/analyze/slot-contract-validator.d.ts +68 -0
- package/dist/analyze/slot-contract-validator.d.ts.map +1 -0
- package/dist/analyze/slot-contract-validator.js +232 -0
- package/dist/analyze/slot-contract-validator.js.map +1 -0
- package/dist/analyze/token-validator.d.ts +62 -0
- package/dist/analyze/token-validator.d.ts.map +1 -0
- package/dist/analyze/token-validator.js +348 -0
- package/dist/analyze/token-validator.js.map +1 -0
- package/dist/cli/brain.d.ts +12 -0
- package/dist/cli/brain.d.ts.map +1 -0
- package/dist/cli/brain.js +641 -0
- package/dist/cli/brain.js.map +1 -0
- package/dist/cli/formatters/json.d.ts +15 -0
- package/dist/cli/formatters/json.d.ts.map +1 -0
- package/dist/cli/formatters/json.js +18 -0
- package/dist/cli/formatters/json.js.map +1 -0
- package/dist/cli/formatters/terminal.d.ts +19 -0
- package/dist/cli/formatters/terminal.d.ts.map +1 -0
- package/dist/cli/formatters/terminal.js +125 -0
- package/dist/cli/formatters/terminal.js.map +1 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +7 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/data/governance-rules.json +94 -0
- package/dist/governance/audit-log.d.ts +17 -0
- package/dist/governance/audit-log.d.ts.map +1 -0
- package/dist/governance/audit-log.js +44 -0
- package/dist/governance/audit-log.js.map +1 -0
- package/dist/governance/index.d.ts +8 -0
- package/dist/governance/index.d.ts.map +1 -0
- package/dist/governance/index.js +8 -0
- package/dist/governance/index.js.map +1 -0
- package/dist/governance/permissions.d.ts +26 -0
- package/dist/governance/permissions.d.ts.map +1 -0
- package/dist/governance/permissions.js +75 -0
- package/dist/governance/permissions.js.map +1 -0
- package/dist/governance/rules-engine.d.ts +24 -0
- package/dist/governance/rules-engine.d.ts.map +1 -0
- package/dist/governance/rules-engine.js +111 -0
- package/dist/governance/rules-engine.js.map +1 -0
- package/dist/governance/trust-manager.d.ts +34 -0
- package/dist/governance/trust-manager.d.ts.map +1 -0
- package/dist/governance/trust-manager.js +148 -0
- package/dist/governance/trust-manager.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/knowledge-graph/component-index.d.ts +320 -0
- package/dist/knowledge-graph/component-index.d.ts.map +1 -0
- package/dist/knowledge-graph/component-index.js +1033 -0
- package/dist/knowledge-graph/component-index.js.map +1 -0
- package/dist/knowledge-graph/index.d.ts +134 -0
- package/dist/knowledge-graph/index.d.ts.map +1 -0
- package/dist/knowledge-graph/index.js +249 -0
- package/dist/knowledge-graph/index.js.map +1 -0
- package/dist/knowledge-graph/learning-history.d.ts +77 -0
- package/dist/knowledge-graph/learning-history.d.ts.map +1 -0
- package/dist/knowledge-graph/learning-history.js +187 -0
- package/dist/knowledge-graph/learning-history.js.map +1 -0
- package/dist/knowledge-graph/relationship-map.d.ts +55 -0
- package/dist/knowledge-graph/relationship-map.d.ts.map +1 -0
- package/dist/knowledge-graph/relationship-map.js +238 -0
- package/dist/knowledge-graph/relationship-map.js.map +1 -0
- package/dist/knowledge-graph/token-taxonomy.d.ts +127 -0
- package/dist/knowledge-graph/token-taxonomy.d.ts.map +1 -0
- package/dist/knowledge-graph/token-taxonomy.js +357 -0
- package/dist/knowledge-graph/token-taxonomy.js.map +1 -0
- package/dist/loop/fix-agent.d.ts +55 -0
- package/dist/loop/fix-agent.d.ts.map +1 -0
- package/dist/loop/fix-agent.js +344 -0
- package/dist/loop/fix-agent.js.map +1 -0
- package/dist/loop/index.d.ts +8 -0
- package/dist/loop/index.d.ts.map +1 -0
- package/dist/loop/index.js +8 -0
- package/dist/loop/index.js.map +1 -0
- package/dist/loop/issue-fetcher.d.ts +51 -0
- package/dist/loop/issue-fetcher.d.ts.map +1 -0
- package/dist/loop/issue-fetcher.js +188 -0
- package/dist/loop/issue-fetcher.js.map +1 -0
- package/dist/loop/observer.d.ts +42 -0
- package/dist/loop/observer.d.ts.map +1 -0
- package/dist/loop/observer.js +220 -0
- package/dist/loop/observer.js.map +1 -0
- package/dist/loop/pacer.d.ts +44 -0
- package/dist/loop/pacer.d.ts.map +1 -0
- package/dist/loop/pacer.js +90 -0
- package/dist/loop/pacer.js.map +1 -0
- package/dist/loop/reporter.d.ts +9 -0
- package/dist/loop/reporter.d.ts.map +1 -0
- package/dist/loop/reporter.js +119 -0
- package/dist/loop/reporter.js.map +1 -0
- package/dist/loop/runner.d.ts +57 -0
- package/dist/loop/runner.d.ts.map +1 -0
- package/dist/loop/runner.js +390 -0
- package/dist/loop/runner.js.map +1 -0
- package/dist/loop/types.d.ts +151 -0
- package/dist/loop/types.d.ts.map +1 -0
- package/dist/loop/types.js +22 -0
- package/dist/loop/types.js.map +1 -0
- package/dist/mcp/index.d.ts +7 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +7 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/server.d.ts +12 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +618 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/pipeline/agent-runner.d.ts +34 -0
- package/dist/pipeline/agent-runner.d.ts.map +1 -0
- package/dist/pipeline/agent-runner.js +323 -0
- package/dist/pipeline/agent-runner.js.map +1 -0
- package/dist/pipeline/agents/accessibility-auditor.d.ts +10 -0
- package/dist/pipeline/agents/accessibility-auditor.d.ts.map +1 -0
- package/dist/pipeline/agents/accessibility-auditor.js +69 -0
- package/dist/pipeline/agents/accessibility-auditor.js.map +1 -0
- package/dist/pipeline/agents/code-reviewer.d.ts +10 -0
- package/dist/pipeline/agents/code-reviewer.d.ts.map +1 -0
- package/dist/pipeline/agents/code-reviewer.js +75 -0
- package/dist/pipeline/agents/code-reviewer.js.map +1 -0
- package/dist/pipeline/agents/code-writer.d.ts +10 -0
- package/dist/pipeline/agents/code-writer.d.ts.map +1 -0
- package/dist/pipeline/agents/code-writer.js +103 -0
- package/dist/pipeline/agents/code-writer.js.map +1 -0
- package/dist/pipeline/agents/component-architect.d.ts +13 -0
- package/dist/pipeline/agents/component-architect.d.ts.map +1 -0
- package/dist/pipeline/agents/component-architect.js +81 -0
- package/dist/pipeline/agents/component-architect.js.map +1 -0
- package/dist/pipeline/agents/index.d.ts +16 -0
- package/dist/pipeline/agents/index.d.ts.map +1 -0
- package/dist/pipeline/agents/index.js +24 -0
- package/dist/pipeline/agents/index.js.map +1 -0
- package/dist/pipeline/agents/library-researcher.d.ts +12 -0
- package/dist/pipeline/agents/library-researcher.d.ts.map +1 -0
- package/dist/pipeline/agents/library-researcher.js +85 -0
- package/dist/pipeline/agents/library-researcher.js.map +1 -0
- package/dist/pipeline/agents/quality-gate.d.ts +9 -0
- package/dist/pipeline/agents/quality-gate.d.ts.map +1 -0
- package/dist/pipeline/agents/quality-gate.js +71 -0
- package/dist/pipeline/agents/quality-gate.js.map +1 -0
- package/dist/pipeline/agents/spec-analyst.d.ts +10 -0
- package/dist/pipeline/agents/spec-analyst.d.ts.map +1 -0
- package/dist/pipeline/agents/spec-analyst.js +72 -0
- package/dist/pipeline/agents/spec-analyst.js.map +1 -0
- package/dist/pipeline/agents/story-author.d.ts +9 -0
- package/dist/pipeline/agents/story-author.d.ts.map +1 -0
- package/dist/pipeline/agents/story-author.js +65 -0
- package/dist/pipeline/agents/story-author.js.map +1 -0
- package/dist/pipeline/artifact-store.d.ts +27 -0
- package/dist/pipeline/artifact-store.d.ts.map +1 -0
- package/dist/pipeline/artifact-store.js +77 -0
- package/dist/pipeline/artifact-store.js.map +1 -0
- package/dist/pipeline/conversational-gate.d.ts +26 -0
- package/dist/pipeline/conversational-gate.d.ts.map +1 -0
- package/dist/pipeline/conversational-gate.js +122 -0
- package/dist/pipeline/conversational-gate.js.map +1 -0
- package/dist/pipeline/index.d.ts +14 -0
- package/dist/pipeline/index.d.ts.map +1 -0
- package/dist/pipeline/index.js +17 -0
- package/dist/pipeline/index.js.map +1 -0
- package/dist/pipeline/iteration-tracker.d.ts +29 -0
- package/dist/pipeline/iteration-tracker.d.ts.map +1 -0
- package/dist/pipeline/iteration-tracker.js +102 -0
- package/dist/pipeline/iteration-tracker.js.map +1 -0
- package/dist/pipeline/learning-bridge.d.ts +37 -0
- package/dist/pipeline/learning-bridge.d.ts.map +1 -0
- package/dist/pipeline/learning-bridge.js +118 -0
- package/dist/pipeline/learning-bridge.js.map +1 -0
- package/dist/pipeline/orchestrator.d.ts +45 -0
- package/dist/pipeline/orchestrator.d.ts.map +1 -0
- package/dist/pipeline/orchestrator.js +473 -0
- package/dist/pipeline/orchestrator.js.map +1 -0
- package/dist/pipeline/templates/architecture.d.ts +27 -0
- package/dist/pipeline/templates/architecture.d.ts.map +1 -0
- package/dist/pipeline/templates/architecture.js +111 -0
- package/dist/pipeline/templates/architecture.js.map +1 -0
- package/dist/pipeline/templates/brief.d.ts +22 -0
- package/dist/pipeline/templates/brief.d.ts.map +1 -0
- package/dist/pipeline/templates/brief.js +121 -0
- package/dist/pipeline/templates/brief.js.map +1 -0
- package/dist/pipeline/templates/component-rules.d.ts +25 -0
- package/dist/pipeline/templates/component-rules.d.ts.map +1 -0
- package/dist/pipeline/templates/component-rules.js +93 -0
- package/dist/pipeline/templates/component-rules.js.map +1 -0
- package/dist/pipeline/templates/index.d.ts +9 -0
- package/dist/pipeline/templates/index.d.ts.map +1 -0
- package/dist/pipeline/templates/index.js +7 -0
- package/dist/pipeline/templates/index.js.map +1 -0
- package/dist/pipeline/tool-handler.d.ts +25 -0
- package/dist/pipeline/tool-handler.d.ts.map +1 -0
- package/dist/pipeline/tool-handler.js +392 -0
- package/dist/pipeline/tool-handler.js.map +1 -0
- package/dist/pipeline/types.d.ts +146 -0
- package/dist/pipeline/types.d.ts.map +1 -0
- package/dist/pipeline/types.js +27 -0
- package/dist/pipeline/types.js.map +1 -0
- package/dist/plan/action-types.d.ts +31 -0
- package/dist/plan/action-types.d.ts.map +1 -0
- package/dist/plan/action-types.js +83 -0
- package/dist/plan/action-types.js.map +1 -0
- package/dist/plan/decision-engine.d.ts +57 -0
- package/dist/plan/decision-engine.d.ts.map +1 -0
- package/dist/plan/decision-engine.js +162 -0
- package/dist/plan/decision-engine.js.map +1 -0
- package/dist/plan/index.d.ts +6 -0
- package/dist/plan/index.d.ts.map +1 -0
- package/dist/plan/index.js +6 -0
- package/dist/plan/index.js.map +1 -0
- package/dist/types.d.ts +351 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +26 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/anthropic.d.ts +15 -0
- package/dist/utils/anthropic.d.ts.map +1 -0
- package/dist/utils/anthropic.js +40 -0
- package/dist/utils/anthropic.js.map +1 -0
- package/dist/utils/id.d.ts +8 -0
- package/dist/utils/id.d.ts.map +1 -0
- package/dist/utils/id.js +14 -0
- package/dist/utils/id.js.map +1 -0
- package/package.json +80 -0
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Naming Validator
|
|
3
|
+
*
|
|
4
|
+
* Validates BEM/BEMIT naming conventions in Eddie components:
|
|
5
|
+
* - CSS class prefixes (ed-c-, ed-l-, ed-u-, ed-r-, ed-p-, etc.)
|
|
6
|
+
* - BEM structure (block__element--modifier)
|
|
7
|
+
* - Detects unclassed HTML elements in Lit component templates
|
|
8
|
+
*
|
|
9
|
+
* Understands Lit-specific patterns:
|
|
10
|
+
* - Only scans inside html`...` tagged template literals
|
|
11
|
+
* - Recognizes dynamic class bindings (class="${expr}")
|
|
12
|
+
* - Skips JSDoc/comment blocks
|
|
13
|
+
* - Skips .stories.ts files for unclassed element checks (slot content is fine)
|
|
14
|
+
*/
|
|
15
|
+
import { readFile } from 'fs/promises';
|
|
16
|
+
import glob from 'fast-glob';
|
|
17
|
+
export class NamingValidator {
|
|
18
|
+
/**
|
|
19
|
+
* Class prefix patterns
|
|
20
|
+
*/
|
|
21
|
+
validPrefixes = [
|
|
22
|
+
/^ed-c-/, // Component
|
|
23
|
+
/^ed-l-/, // Layout
|
|
24
|
+
/^ed-u-/, // Utility
|
|
25
|
+
/^is-/, // State
|
|
26
|
+
/^has-/, // State (has)
|
|
27
|
+
/^ed-r-/, // Recipe
|
|
28
|
+
/^ed-p-/, // Page
|
|
29
|
+
];
|
|
30
|
+
/**
|
|
31
|
+
* HTML elements that can exist without a class
|
|
32
|
+
* - SVG internals, slots, templates, scripts, style tags
|
|
33
|
+
* - Custom elements (contain a hyphen) always skip
|
|
34
|
+
*/
|
|
35
|
+
unclassedAllowed = new Set([
|
|
36
|
+
'svg', 'path', 'g', 'circle', 'rect', 'ellipse', 'line',
|
|
37
|
+
'polyline', 'polygon', 'text', 'tspan', 'image', 'use',
|
|
38
|
+
'defs', 'symbol', 'marker', 'linearGradient', 'radialGradient',
|
|
39
|
+
'stop', 'clipPath', 'mask', 'pattern',
|
|
40
|
+
'style', 'script', 'template', 'slot',
|
|
41
|
+
// Lit-specific
|
|
42
|
+
'br', 'hr', 'img', 'input', 'meta', 'link', 'source', 'track', 'wbr',
|
|
43
|
+
]);
|
|
44
|
+
/**
|
|
45
|
+
* Validate a single component file
|
|
46
|
+
*/
|
|
47
|
+
async validateFile(filePath) {
|
|
48
|
+
try {
|
|
49
|
+
const content = await readFile(filePath, 'utf-8');
|
|
50
|
+
const issues = [];
|
|
51
|
+
const isTemplate = filePath.endsWith('.ts') || filePath.endsWith('.tsx');
|
|
52
|
+
const isStyle = filePath.endsWith('.scss');
|
|
53
|
+
const isStories = filePath.includes('.stories.');
|
|
54
|
+
if (isTemplate) {
|
|
55
|
+
issues.push(...this.validateTemplate(filePath, content, isStories));
|
|
56
|
+
}
|
|
57
|
+
if (isStyle) {
|
|
58
|
+
issues.push(...this.validateStyleFile(filePath, content));
|
|
59
|
+
}
|
|
60
|
+
return issues;
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
return [{
|
|
64
|
+
id: `naming-validate-error-${Date.now()}`,
|
|
65
|
+
category: 'naming',
|
|
66
|
+
severity: 'error',
|
|
67
|
+
message: `Failed to validate file: ${error instanceof Error ? error.message : String(error)}`,
|
|
68
|
+
file: filePath,
|
|
69
|
+
autoFixable: false,
|
|
70
|
+
}];
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Validate all component files in a directory
|
|
75
|
+
*/
|
|
76
|
+
async validateDirectory(dirPath) {
|
|
77
|
+
const componentFiles = await glob(['**/*.ts', '**/*.tsx', '**/*.scss'], {
|
|
78
|
+
cwd: dirPath,
|
|
79
|
+
absolute: true,
|
|
80
|
+
ignore: ['**/node_modules/**', '**/*.d.ts'],
|
|
81
|
+
});
|
|
82
|
+
const allIssues = [];
|
|
83
|
+
for (const file of componentFiles) {
|
|
84
|
+
const issues = await this.validateFile(file);
|
|
85
|
+
allIssues.push(...issues);
|
|
86
|
+
}
|
|
87
|
+
return allIssues;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Validate a single CSS class name string
|
|
91
|
+
*/
|
|
92
|
+
validateClassName(className) {
|
|
93
|
+
const result = this.classNameValidation(className);
|
|
94
|
+
if (!result.valid) {
|
|
95
|
+
return {
|
|
96
|
+
id: `naming-class-${className}`,
|
|
97
|
+
category: 'naming',
|
|
98
|
+
severity: 'error',
|
|
99
|
+
message: `Invalid class name "${className}": ${result.issues.join('; ')}`,
|
|
100
|
+
actual: className,
|
|
101
|
+
expected: this.suggestClassName(className),
|
|
102
|
+
autoFixable: false,
|
|
103
|
+
file: 'unknown',
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
// ===========================================================================
|
|
109
|
+
// Template validation — Lit-aware
|
|
110
|
+
// ===========================================================================
|
|
111
|
+
/**
|
|
112
|
+
* Validate HTML elements inside Lit templates.
|
|
113
|
+
*
|
|
114
|
+
* Key behaviors:
|
|
115
|
+
* - Extracts only the content inside html`...` tagged template literals
|
|
116
|
+
* - Strips JSDoc/block comments before scanning
|
|
117
|
+
* - Recognizes dynamic class bindings: class="${...}" counts as having a class
|
|
118
|
+
* - Skips unclassed-element checks in .stories.ts (slot content is fine)
|
|
119
|
+
* - Skips self-closing void elements and custom elements (contain hyphen)
|
|
120
|
+
*/
|
|
121
|
+
validateTemplate(filePath, content, isStories) {
|
|
122
|
+
const issues = [];
|
|
123
|
+
// Strip all block comments (/** ... */, /* ... */) to avoid scanning JSDoc
|
|
124
|
+
const stripped = this.stripBlockComments(content);
|
|
125
|
+
// Extract html`...` template regions with their starting line offsets
|
|
126
|
+
const templateRegions = this.extractLitTemplates(stripped);
|
|
127
|
+
if (templateRegions.length === 0) {
|
|
128
|
+
// No Lit templates found — nothing to validate for unclassed elements
|
|
129
|
+
// Still check any static class="" attributes in the whole file
|
|
130
|
+
return issues;
|
|
131
|
+
}
|
|
132
|
+
for (const region of templateRegions) {
|
|
133
|
+
// Check for unclassed elements (skip in stories files)
|
|
134
|
+
if (!isStories) {
|
|
135
|
+
issues.push(...this.checkUnclassedElements(filePath, region.content, region.startLine));
|
|
136
|
+
}
|
|
137
|
+
// Check static class names in templates
|
|
138
|
+
issues.push(...this.checkStaticClassNames(filePath, region.content, region.startLine));
|
|
139
|
+
}
|
|
140
|
+
return issues;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Strip block comments (/* ... * /) including JSDoc (/** ... * /)
|
|
144
|
+
* Preserves line count by replacing comment content with blank lines.
|
|
145
|
+
*/
|
|
146
|
+
stripBlockComments(content) {
|
|
147
|
+
// Replace block comments with the same number of newlines to preserve line numbering
|
|
148
|
+
return content.replace(/\/\*[\s\S]*?\*\//g, (match) => {
|
|
149
|
+
const newlineCount = (match.match(/\n/g) || []).length;
|
|
150
|
+
return '\n'.repeat(newlineCount);
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Extract content from html`...` tagged template literals.
|
|
155
|
+
* Returns regions with their content and the line offset where they start.
|
|
156
|
+
*/
|
|
157
|
+
extractLitTemplates(content) {
|
|
158
|
+
const regions = [];
|
|
159
|
+
// Match html`...` with support for nested ${} expressions
|
|
160
|
+
// We use a state machine approach since regex can't handle nested backticks well
|
|
161
|
+
const lines = content.split('\n');
|
|
162
|
+
let inTemplate = false;
|
|
163
|
+
let depth = 0;
|
|
164
|
+
let templateLines = [];
|
|
165
|
+
let templateStartLine = 0;
|
|
166
|
+
for (let i = 0; i < lines.length; i++) {
|
|
167
|
+
const line = lines[i];
|
|
168
|
+
// Skip single-line comments
|
|
169
|
+
const trimmed = line.trim();
|
|
170
|
+
if (trimmed.startsWith('//'))
|
|
171
|
+
continue;
|
|
172
|
+
if (!inTemplate) {
|
|
173
|
+
// Look for html` start
|
|
174
|
+
const htmlStart = line.indexOf('html`');
|
|
175
|
+
if (htmlStart !== -1) {
|
|
176
|
+
inTemplate = true;
|
|
177
|
+
depth = 1;
|
|
178
|
+
templateStartLine = i;
|
|
179
|
+
// Get the part after html`
|
|
180
|
+
const afterStart = line.substring(htmlStart + 5);
|
|
181
|
+
// Count backticks to track depth (simplified — handles most cases)
|
|
182
|
+
templateLines = [afterStart];
|
|
183
|
+
// Check if template closes on same line
|
|
184
|
+
if (this.templateClosesOnLine(afterStart)) {
|
|
185
|
+
inTemplate = false;
|
|
186
|
+
regions.push({
|
|
187
|
+
content: templateLines.join('\n'),
|
|
188
|
+
startLine: templateStartLine,
|
|
189
|
+
});
|
|
190
|
+
templateLines = [];
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
templateLines.push(line);
|
|
196
|
+
// Check if template closes on this line
|
|
197
|
+
// Look for a backtick that's not inside a ${} expression
|
|
198
|
+
if (this.templateClosesOnLine(line)) {
|
|
199
|
+
inTemplate = false;
|
|
200
|
+
regions.push({
|
|
201
|
+
content: templateLines.join('\n'),
|
|
202
|
+
startLine: templateStartLine,
|
|
203
|
+
});
|
|
204
|
+
templateLines = [];
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return regions;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Simplified check: does a template close on this line?
|
|
212
|
+
* Looks for a ` that's not preceded by ${ context.
|
|
213
|
+
*/
|
|
214
|
+
templateClosesOnLine(line) {
|
|
215
|
+
// Count unescaped backticks not inside ${}
|
|
216
|
+
let inExpr = 0;
|
|
217
|
+
for (let i = 0; i < line.length; i++) {
|
|
218
|
+
if (line[i] === '$' && line[i + 1] === '{') {
|
|
219
|
+
inExpr++;
|
|
220
|
+
i++; // skip {
|
|
221
|
+
}
|
|
222
|
+
else if (line[i] === '}' && inExpr > 0) {
|
|
223
|
+
inExpr--;
|
|
224
|
+
}
|
|
225
|
+
else if (line[i] === '`' && inExpr === 0) {
|
|
226
|
+
return true;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Check for HTML elements without classes in template content.
|
|
233
|
+
*
|
|
234
|
+
* Understands:
|
|
235
|
+
* - class="${expr}" — dynamic class, counts as having a class
|
|
236
|
+
* - class="literal" — static class
|
|
237
|
+
* - styleModifier="..." — Eddie's pattern for passing classes to children
|
|
238
|
+
* - Tags with a hyphen are custom elements (always skip)
|
|
239
|
+
*/
|
|
240
|
+
checkUnclassedElements(filePath, templateContent, startLine) {
|
|
241
|
+
const issues = [];
|
|
242
|
+
const lines = templateContent.split('\n');
|
|
243
|
+
// Skip TextPassage (prose content is allowed unclassed) and test files
|
|
244
|
+
if (filePath.includes('text-passage') ||
|
|
245
|
+
filePath.includes('TextPassage') ||
|
|
246
|
+
filePath.includes('.test.') ||
|
|
247
|
+
filePath.includes('.spec.')) {
|
|
248
|
+
return issues;
|
|
249
|
+
}
|
|
250
|
+
for (let i = 0; i < lines.length; i++) {
|
|
251
|
+
const line = lines[i];
|
|
252
|
+
// Find opening HTML tags — include hyphens in tag name to capture custom elements
|
|
253
|
+
const tagMatches = line.matchAll(/<([a-zA-Z][a-zA-Z0-9-]*)\b([^>]*?)>/g);
|
|
254
|
+
for (const match of tagMatches) {
|
|
255
|
+
const tagName = match[1].toLowerCase();
|
|
256
|
+
const attributes = match[2];
|
|
257
|
+
// Skip: custom elements (have hyphen), void elements, allowed elements
|
|
258
|
+
if (tagName.includes('-'))
|
|
259
|
+
continue;
|
|
260
|
+
if (this.unclassedAllowed.has(tagName))
|
|
261
|
+
continue;
|
|
262
|
+
// Check if element has a class attribute (static or dynamic)
|
|
263
|
+
const hasClass = /class\s*=/.test(attributes) || // class="..." or class="${...}"
|
|
264
|
+
/styleModifier\s*=/.test(attributes) || // Eddie's styleModifier pattern
|
|
265
|
+
/\$\{.*class/.test(attributes); // ${classMap(...)} etc.
|
|
266
|
+
if (!hasClass) {
|
|
267
|
+
issues.push({
|
|
268
|
+
id: `naming-unclassed-${tagName}-${startLine + i}`,
|
|
269
|
+
category: 'naming',
|
|
270
|
+
severity: 'warning',
|
|
271
|
+
message: `Unclassed <${tagName}> element in Lit template. Consider adding a BEM class.`,
|
|
272
|
+
file: filePath,
|
|
273
|
+
line: startLine + i + 1,
|
|
274
|
+
actual: `<${tagName}>`,
|
|
275
|
+
expected: `<${tagName} class="ed-c-...">`,
|
|
276
|
+
autoFixable: false,
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return issues;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Check static class name values in templates for Eddie naming compliance.
|
|
285
|
+
* Only validates literal class strings, not dynamic ${} expressions.
|
|
286
|
+
*/
|
|
287
|
+
checkStaticClassNames(filePath, templateContent, startLine) {
|
|
288
|
+
const issues = [];
|
|
289
|
+
const lines = templateContent.split('\n');
|
|
290
|
+
for (let i = 0; i < lines.length; i++) {
|
|
291
|
+
const line = lines[i];
|
|
292
|
+
// Match static class="literal-value" (not class="${dynamic}")
|
|
293
|
+
const staticClassMatches = line.matchAll(/class\s*=\s*"([^"$]+)"/g);
|
|
294
|
+
for (const match of staticClassMatches) {
|
|
295
|
+
const classValue = match[1].trim();
|
|
296
|
+
if (!classValue)
|
|
297
|
+
continue;
|
|
298
|
+
const classNames = classValue.split(/\s+/);
|
|
299
|
+
for (const className of classNames) {
|
|
300
|
+
if (!className)
|
|
301
|
+
continue;
|
|
302
|
+
// Skip Lit expressions that leaked through
|
|
303
|
+
if (className.includes('${') || className.includes('}'))
|
|
304
|
+
continue;
|
|
305
|
+
const result = this.classNameValidation(className);
|
|
306
|
+
if (!result.valid) {
|
|
307
|
+
issues.push({
|
|
308
|
+
id: `naming-class-${className}-${startLine + i}`,
|
|
309
|
+
category: 'naming',
|
|
310
|
+
severity: 'error',
|
|
311
|
+
message: `Invalid class name "${className}" in template. ${result.issues.join('; ')}`,
|
|
312
|
+
file: filePath,
|
|
313
|
+
line: startLine + i + 1,
|
|
314
|
+
actual: className,
|
|
315
|
+
expected: this.suggestClassName(className),
|
|
316
|
+
autoFixable: true,
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return issues;
|
|
323
|
+
}
|
|
324
|
+
// ===========================================================================
|
|
325
|
+
// SCSS validation
|
|
326
|
+
// ===========================================================================
|
|
327
|
+
/**
|
|
328
|
+
* Validate class names in SCSS file
|
|
329
|
+
*/
|
|
330
|
+
validateStyleFile(filePath, content) {
|
|
331
|
+
const issues = [];
|
|
332
|
+
const lines = content.split('\n');
|
|
333
|
+
let inBlockComment = false;
|
|
334
|
+
lines.forEach((line, lineIndex) => {
|
|
335
|
+
const trimmed = line.trim();
|
|
336
|
+
// Track block comments
|
|
337
|
+
if (trimmed.includes('/*'))
|
|
338
|
+
inBlockComment = true;
|
|
339
|
+
if (trimmed.includes('*/')) {
|
|
340
|
+
inBlockComment = false;
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
if (inBlockComment)
|
|
344
|
+
return;
|
|
345
|
+
// Skip single-line comments
|
|
346
|
+
if (trimmed.startsWith('//'))
|
|
347
|
+
return;
|
|
348
|
+
// Find class definitions (.className { or .className &)
|
|
349
|
+
const classDefMatches = line.matchAll(/\.([a-zA-Z][a-zA-Z0-9_-]*)\s*[{&,]/g);
|
|
350
|
+
for (const match of classDefMatches) {
|
|
351
|
+
const className = match[1];
|
|
352
|
+
// Skip Sass variables and interpolations
|
|
353
|
+
if (className.startsWith('$') || className.includes('#{'))
|
|
354
|
+
continue;
|
|
355
|
+
const result = this.classNameValidation(className);
|
|
356
|
+
if (!result.valid) {
|
|
357
|
+
issues.push({
|
|
358
|
+
id: `naming-scss-class-${className}-${lineIndex}`,
|
|
359
|
+
category: 'naming',
|
|
360
|
+
severity: 'error',
|
|
361
|
+
message: `Invalid class name ".${className}" in SCSS. ${result.issues.join('; ')}`,
|
|
362
|
+
file: filePath,
|
|
363
|
+
line: lineIndex + 1,
|
|
364
|
+
actual: className,
|
|
365
|
+
expected: this.suggestClassName(className),
|
|
366
|
+
autoFixable: false,
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
return issues;
|
|
372
|
+
}
|
|
373
|
+
// ===========================================================================
|
|
374
|
+
// Class name validation logic
|
|
375
|
+
// ===========================================================================
|
|
376
|
+
/**
|
|
377
|
+
* Validate a single class name string against Eddie's BEM/BEMIT conventions
|
|
378
|
+
*/
|
|
379
|
+
classNameValidation(className) {
|
|
380
|
+
const issues = [];
|
|
381
|
+
if (!className) {
|
|
382
|
+
return { valid: false, issues: ['Class name is empty'] };
|
|
383
|
+
}
|
|
384
|
+
// Check for valid prefix
|
|
385
|
+
const hasValidPrefix = this.validPrefixes.some((pattern) => pattern.test(className));
|
|
386
|
+
if (!hasValidPrefix) {
|
|
387
|
+
issues.push('Does not start with valid prefix (ed-c-, ed-l-, ed-u-, ed-r-, ed-p-, is-, has-)');
|
|
388
|
+
}
|
|
389
|
+
// Extract the part after prefix for BEM validation
|
|
390
|
+
let bemPart = className;
|
|
391
|
+
for (const prefix of this.validPrefixes) {
|
|
392
|
+
const match = className.match(prefix);
|
|
393
|
+
if (match) {
|
|
394
|
+
bemPart = className.slice(match[0].length);
|
|
395
|
+
break;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
// Validate BEM structure (block__element--modifier)
|
|
399
|
+
if (bemPart && !this.validateBemStructure(bemPart)) {
|
|
400
|
+
issues.push('Invalid BEM structure. Use block, block__element, or block--modifier format');
|
|
401
|
+
}
|
|
402
|
+
// Check for uppercase letters (BEM uses lowercase only)
|
|
403
|
+
if (/[A-Z]/.test(className)) {
|
|
404
|
+
issues.push('BEM classes must be lowercase');
|
|
405
|
+
}
|
|
406
|
+
return {
|
|
407
|
+
valid: issues.length === 0,
|
|
408
|
+
issues,
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Validate BEM structure (block__element--modifier)
|
|
413
|
+
* Allows: block, block__element, block--modifier, block__element--modifier
|
|
414
|
+
* Parts can contain hyphens (single) for multi-word names.
|
|
415
|
+
*/
|
|
416
|
+
validateBemStructure(bemPart) {
|
|
417
|
+
const bemRegex = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*(?:__[a-z][a-z0-9]*(?:-[a-z0-9]+)*)?(?:--[a-z][a-z0-9]*(?:-[a-z0-9]+)*)?$/;
|
|
418
|
+
return bemRegex.test(bemPart);
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Suggest a corrected class name
|
|
422
|
+
*/
|
|
423
|
+
suggestClassName(className) {
|
|
424
|
+
if (!this.validPrefixes.some((p) => p.test(className))) {
|
|
425
|
+
return `ed-c-${className.toLowerCase()}`;
|
|
426
|
+
}
|
|
427
|
+
return className.toLowerCase().replace(/[^a-z0-9_-]/g, '');
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
//# sourceMappingURL=naming-validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"naming-validator.js","sourceRoot":"","sources":["../../src/analyze/naming-validator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slot Contract Validator
|
|
3
|
+
*
|
|
4
|
+
* Detects the silent-drop class of bug filed under #595, #627, #639: Eddie
|
|
5
|
+
* markup that uses a `slot="X"` attribute referencing a slot the component
|
|
6
|
+
* doesn't declare. The unknown slot reaches the shadow DOM with no matching
|
|
7
|
+
* `<slot name="X">`, the content is dropped, and nothing in the browser
|
|
8
|
+
* surfaces the failure.
|
|
9
|
+
*
|
|
10
|
+
* Scope:
|
|
11
|
+
* - `.html` files (consumer projects, boilerplates)
|
|
12
|
+
* - Lit `html\`...\`` template regions inside `.ts` / `.tsx` files
|
|
13
|
+
*
|
|
14
|
+
* Strategy:
|
|
15
|
+
* - Find every `<ed-...>` opening tag
|
|
16
|
+
* - Walk forward to its matching close tag (depth-tracked)
|
|
17
|
+
* - Inside that range, find every immediate-child element with a
|
|
18
|
+
* `slot="NAME"` attribute
|
|
19
|
+
* - Look up the parent component in the index; if NAME is not in the
|
|
20
|
+
* declared slot list, emit a finding
|
|
21
|
+
*
|
|
22
|
+
* The check is intentionally conservative: false positives are worse than
|
|
23
|
+
* false negatives because validators run in tight CI loops and noisy
|
|
24
|
+
* output gets ignored. Components missing from the index are skipped
|
|
25
|
+
* (rather than flagged as unknown), and the regex is forgiving about
|
|
26
|
+
* whitespace and quote style.
|
|
27
|
+
*/
|
|
28
|
+
import type { ComponentEntry, HealthIssue } from '../types.js';
|
|
29
|
+
interface ComponentLookup {
|
|
30
|
+
getComponent(tagName: string): ComponentEntry | undefined;
|
|
31
|
+
}
|
|
32
|
+
export declare class SlotContractValidator {
|
|
33
|
+
private readonly index;
|
|
34
|
+
constructor(index: ComponentLookup);
|
|
35
|
+
/**
|
|
36
|
+
* Validate a single file for slot-contract violations.
|
|
37
|
+
* Returns an empty array for files we don't scan (e.g. .scss).
|
|
38
|
+
*/
|
|
39
|
+
validateFile(filePath: string): Promise<HealthIssue[]>;
|
|
40
|
+
/**
|
|
41
|
+
* Scan one chunk of markup for slot-contract violations.
|
|
42
|
+
* `startLine` is the 0-based line offset of the chunk within the file —
|
|
43
|
+
* issue line numbers are reported relative to the file, not the chunk.
|
|
44
|
+
*/
|
|
45
|
+
private scanRegion;
|
|
46
|
+
/**
|
|
47
|
+
* Look up the component, then scan the inner markup for any
|
|
48
|
+
* `slot="NAME"` attribute. Emit a finding for each NAME that the
|
|
49
|
+
* component doesn't declare.
|
|
50
|
+
*/
|
|
51
|
+
private scanInner;
|
|
52
|
+
/**
|
|
53
|
+
* Walk forward from `startIdx` to find the matching `</tag>` for an
|
|
54
|
+
* already-opened `<tag …>`. Tracks open/close depth so nested same-named
|
|
55
|
+
* tags don't fool the matcher.
|
|
56
|
+
*/
|
|
57
|
+
private findMatchingClose;
|
|
58
|
+
/**
|
|
59
|
+
* Extract Lit `html\`...\`` template regions from a TS/TSX file.
|
|
60
|
+
* Mirrors NamingValidator.extractLitTemplates, but stripped down — we
|
|
61
|
+
* don't need to handle every nesting edge case, just the common ones.
|
|
62
|
+
*/
|
|
63
|
+
private extractLitTemplates;
|
|
64
|
+
private templateClosesOnLine;
|
|
65
|
+
private lineOf;
|
|
66
|
+
}
|
|
67
|
+
export {};
|
|
68
|
+
//# sourceMappingURL=slot-contract-validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slot-contract-validator.d.ts","sourceRoot":"","sources":["../../src/analyze/slot-contract-validator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/D,UAAU,eAAe;IACvB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAAC;CAC3D;AAED,qBAAa,qBAAqB;IACpB,OAAO,CAAC,QAAQ,CAAC,KAAK;gBAAL,KAAK,EAAE,eAAe;IAEnD;;;OAGG;IACG,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAgC5D;;;;OAIG;IACH,OAAO,CAAC,UAAU;IA+BlB;;;;OAIG;IACH,OAAO,CAAC,SAAS;IA6CjB;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAwBzB;;;;OAIG;IACH,OAAO,CAAC,mBAAmB;IAsC3B,OAAO,CAAC,oBAAoB;IAe5B,OAAO,CAAC,MAAM;CAOf"}
|