@ryuenn3123/agentic-senior-core 3.0.50 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agent-context/review-checklists/pr-checklist.md +1 -0
- package/.agent-context/rules/api-docs.md +63 -47
- package/.agent-context/rules/architecture.md +133 -120
- package/.agent-context/rules/database-design.md +36 -18
- package/.agent-context/rules/docker-runtime.md +66 -43
- package/.agent-context/rules/efficiency-vs-hype.md +38 -17
- package/.agent-context/rules/error-handling.md +35 -16
- package/.agent-context/rules/event-driven.md +35 -18
- package/.agent-context/rules/frontend-architecture.md +103 -76
- package/.agent-context/rules/git-workflow.md +81 -197
- package/.agent-context/rules/microservices.md +42 -41
- package/.agent-context/rules/naming-conv.md +27 -8
- package/.agent-context/rules/performance.md +32 -12
- package/.agent-context/rules/realtime.md +26 -9
- package/.agent-context/rules/security.md +39 -20
- package/.agent-context/rules/testing.md +36 -16
- package/AGENTS.md +9 -9
- package/README.md +10 -1
- package/lib/cli/commands/init.mjs +1 -0
- package/lib/cli/compiler.mjs +1 -0
- package/lib/cli/detector/constants.mjs +135 -0
- package/lib/cli/detector/design-evidence/collector.mjs +256 -0
- package/lib/cli/detector/design-evidence/constants.mjs +39 -0
- package/lib/cli/detector/design-evidence/file-traversal.mjs +83 -0
- package/lib/cli/detector/design-evidence/structured-attribute-evidence.mjs +117 -0
- package/lib/cli/detector/design-evidence/summary.mjs +109 -0
- package/lib/cli/detector/design-evidence/utility-helpers.mjs +122 -0
- package/lib/cli/detector/design-evidence.mjs +25 -610
- package/lib/cli/detector/stack-detection.mjs +243 -0
- package/lib/cli/detector/ui-signals.mjs +150 -0
- package/lib/cli/detector/workspace-scan.mjs +177 -0
- package/lib/cli/detector.mjs +20 -688
- package/lib/cli/memory-continuity.mjs +1 -0
- package/lib/cli/project-scaffolder/design-contract/sections/audits.mjs +96 -0
- package/lib/cli/project-scaffolder/design-contract/sections/conceptual-anchor.mjs +116 -0
- package/lib/cli/project-scaffolder/design-contract/sections/execution-handoff.mjs +211 -0
- package/lib/cli/project-scaffolder/design-contract/seed-signals.mjs +79 -0
- package/lib/cli/project-scaffolder/design-contract/signal-vocab.mjs +64 -0
- package/lib/cli/project-scaffolder/design-contract/validation/anchor-validators.mjs +222 -0
- package/lib/cli/project-scaffolder/design-contract/validation/audit-validators.mjs +117 -0
- package/lib/cli/project-scaffolder/design-contract/validation/completeness.mjs +83 -0
- package/lib/cli/project-scaffolder/design-contract/validation/execution-validators.mjs +328 -0
- package/lib/cli/project-scaffolder/design-contract/validation/helpers.mjs +8 -0
- package/lib/cli/project-scaffolder/design-contract/validation/structural-validators.mjs +79 -0
- package/lib/cli/project-scaffolder/design-contract/validation/system-validators.mjs +256 -0
- package/lib/cli/project-scaffolder/design-contract/validation.mjs +59 -896
- package/lib/cli/project-scaffolder/design-contract.mjs +147 -557
- package/mcp.json +30 -9
- package/package.json +17 -2
- package/scripts/audit-cache-layer-contract.mjs +258 -0
- package/scripts/audit-caching-scope-hygiene.mjs +263 -0
- package/scripts/audit-file-size.mjs +219 -0
- package/scripts/audit-reflection-citations.mjs +163 -0
- package/scripts/audit-release-bundle.mjs +170 -0
- package/scripts/audit-rule-id-uniqueness.mjs +313 -0
- package/scripts/benchmark-evidence-bundle.mjs +1 -0
- package/scripts/build-release-benchmark-bundle.mjs +204 -0
- package/scripts/context-triggered-audit.mjs +1 -0
- package/scripts/documentation-boundary-audit.mjs +1 -0
- package/scripts/explain-on-demand-audit.mjs +2 -1
- package/scripts/frontend-usability-audit.mjs +10 -10
- package/scripts/llm-judge/checklist-loader.mjs +45 -0
- package/scripts/llm-judge/constants.mjs +66 -0
- package/scripts/llm-judge/diff-collection.mjs +74 -0
- package/scripts/llm-judge/prompting.mjs +78 -0
- package/scripts/llm-judge/providers.mjs +111 -0
- package/scripts/llm-judge/verdict.mjs +134 -0
- package/scripts/llm-judge.mjs +21 -482
- package/scripts/mcp-server/tool-registry.mjs +55 -0
- package/scripts/mcp-server/tools.mjs +137 -1
- package/scripts/migrate-rule-format/id-prefix-table.mjs +37 -0
- package/scripts/migrate-rule-format/parse-legacy.mjs +180 -0
- package/scripts/migrate-rule-format/render-new.mjs +169 -0
- package/scripts/migrate-rule-format/roundtrip-validate.mjs +89 -0
- package/scripts/migrate-rule-format.mjs +192 -0
- package/scripts/release-gate/constants.mjs +1 -1
- package/scripts/release-gate/static-checks.mjs +1 -1
- package/scripts/rules-guardian-audit.mjs +5 -2
- package/scripts/single-source-lazy-loading-audit.mjs +2 -1
- package/scripts/ui-design-judge/git-input.mjs +3 -0
- package/scripts/validate/config.mjs +3 -2
- package/scripts/validate/coverage-checks.mjs +1 -1
- package/scripts/validate.mjs +93 -1
|
@@ -1,610 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const DURATION_PATTERN = /\b\d+(?:\.\d+)?m?s\b/g;
|
|
27
|
-
const MEDIA_WIDTH_PATTERN = /\((?:min|max)-width:\s*([0-9.]+(?:px|rem|em))\)/g;
|
|
28
|
-
const STRING_CLASS_ATTRIBUTE_PATTERN = /\b(?:className|class)\s*=\s*(?:"([^"]*)"|'([^']*)'|\{`([^`]*)`\}|\{"([^"]*)"\}|\{'([^']*)'\})/g;
|
|
29
|
-
const EXPRESSION_CLASS_ATTRIBUTE_PATTERN = /\b(?:className|class|:class)\s*=\s*\{([^}\n]+)\}/g;
|
|
30
|
-
const JSX_INLINE_STYLE_PATTERN = /\bstyle\s*=\s*\{\{([\s\S]*?)\}\}/g;
|
|
31
|
-
const VUE_INLINE_STYLE_PATTERN = /\b:style\s*=\s*["']\{([^"']*)\}["']/g;
|
|
32
|
-
|
|
33
|
-
async function collectFrontendSourceFilePaths(directoryPath, collectedFilePaths = []) {
|
|
34
|
-
if (collectedFilePaths.length >= FRONTEND_FILE_SCAN_LIMIT) {
|
|
35
|
-
return collectedFilePaths;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
let directoryEntries;
|
|
39
|
-
try {
|
|
40
|
-
directoryEntries = await fs.readdir(directoryPath, { withFileTypes: true });
|
|
41
|
-
} catch {
|
|
42
|
-
return collectedFilePaths;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
for (const directoryEntry of directoryEntries) {
|
|
46
|
-
if (collectedFilePaths.length >= FRONTEND_FILE_SCAN_LIMIT) {
|
|
47
|
-
break;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (directoryEntry.isDirectory()) {
|
|
51
|
-
if (FRONTEND_SCAN_IGNORE_DIRECTORY_NAMES.has(directoryEntry.name)) {
|
|
52
|
-
continue;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
await collectFrontendSourceFilePaths(path.join(directoryPath, directoryEntry.name), collectedFilePaths);
|
|
56
|
-
continue;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const fileExtension = path.extname(directoryEntry.name).toLowerCase();
|
|
60
|
-
if (FRONTEND_SCAN_FILE_EXTENSIONS.has(fileExtension)) {
|
|
61
|
-
collectedFilePaths.push(path.join(directoryPath, directoryEntry.name));
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return collectedFilePaths;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function countPatternMatches(sourceText, pattern) {
|
|
69
|
-
return Array.from(sourceText.matchAll(pattern)).length;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function pushSampleValue(targetSamples, value, targetSet) {
|
|
73
|
-
const normalizedValue = String(value || '').trim();
|
|
74
|
-
if (!normalizedValue || targetSet.has(normalizedValue)) {
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
targetSet.add(normalizedValue);
|
|
79
|
-
if (targetSamples.length < DESIGN_EVIDENCE_SAMPLE_LIMIT) {
|
|
80
|
-
targetSamples.push(normalizedValue);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function categorizeCssVariable(variableName) {
|
|
85
|
-
const normalizedVariableName = String(variableName || '').trim().toLowerCase();
|
|
86
|
-
|
|
87
|
-
if (/(color|surface|accent|bg|text|border|fill|stroke|ink|tone)/.test(normalizedVariableName)) {
|
|
88
|
-
return 'color';
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (/(space|gap|padding|margin|size|width|height|inset)/.test(normalizedVariableName)) {
|
|
92
|
-
return 'spacing';
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (/(radius|rounded|corner)/.test(normalizedVariableName)) {
|
|
96
|
-
return 'radius';
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (/(shadow|elevation)/.test(normalizedVariableName)) {
|
|
100
|
-
return 'shadow';
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (/(font|type|line|letter|tracking|leading)/.test(normalizedVariableName)) {
|
|
104
|
-
return 'typography';
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (/(motion|duration|easing|ease|animation|transition)/.test(normalizedVariableName)) {
|
|
108
|
-
return 'motion';
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return 'other';
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function inferColorKind(colorValue) {
|
|
115
|
-
if (/^#/i.test(colorValue)) {
|
|
116
|
-
return 'hex';
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (/^rgba?\(/i.test(colorValue)) {
|
|
120
|
-
return 'rgb';
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
if (/^hsla?\(/i.test(colorValue)) {
|
|
124
|
-
return 'hsl';
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (/^oklch\(/i.test(colorValue)) {
|
|
128
|
-
return 'oklch';
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return 'other';
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function incrementCountMap(countMap, key) {
|
|
135
|
-
countMap[key] = (countMap[key] || 0) + 1;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function getFirstDefinedCapture(matchGroups) {
|
|
139
|
-
for (const capturedValue of matchGroups) {
|
|
140
|
-
if (typeof capturedValue === 'string' && capturedValue.trim().length > 0) {
|
|
141
|
-
return capturedValue;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
return '';
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
function normalizeEvidenceSample(rawValue) {
|
|
149
|
-
return String(rawValue || '').replace(/\s+/g, ' ').trim();
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
function inferUtilityFamily(rawUtilityToken) {
|
|
153
|
-
const normalizedUtilityToken = String(rawUtilityToken || '')
|
|
154
|
-
.trim()
|
|
155
|
-
.split(':')
|
|
156
|
-
.pop()
|
|
157
|
-
?.replace(/^!/, '')
|
|
158
|
-
?.replace(/\/.+$/, '')
|
|
159
|
-
?.trim();
|
|
160
|
-
|
|
161
|
-
if (!normalizedUtilityToken) {
|
|
162
|
-
return '';
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
if (/^grid(?:-|$)/.test(normalizedUtilityToken)) {
|
|
166
|
-
return 'grid';
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
if (/^flex(?:-|$)/.test(normalizedUtilityToken)) {
|
|
170
|
-
return 'flex';
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
if (/^(?:block|inline|hidden|contents)$/.test(normalizedUtilityToken)) {
|
|
174
|
-
return normalizedUtilityToken;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
if (/^(?:min|max)-\[/.test(normalizedUtilityToken)) {
|
|
178
|
-
return 'arbitrary-breakpoint';
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
return normalizedUtilityToken.split('-')[0];
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
function collectTailwindUtilityFamilies(classValue, utilityFamilyCounts, utilityFamilySamples, utilityFamilySampleSet) {
|
|
185
|
-
for (const utilityToken of String(classValue || '').split(/\s+/g)) {
|
|
186
|
-
const utilityFamily = inferUtilityFamily(utilityToken);
|
|
187
|
-
if (!utilityFamily) {
|
|
188
|
-
continue;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
incrementCountMap(utilityFamilyCounts, utilityFamily);
|
|
192
|
-
pushSampleValue(utilityFamilySamples, utilityFamily, utilityFamilySampleSet);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
function applyInlineStyleInspection(styleSourceText, summary, styleSampleSet) {
|
|
197
|
-
const normalizedStyleSource = normalizeEvidenceSample(styleSourceText);
|
|
198
|
-
pushSampleValue(summary.structuredInspection.inlineStyleSamples, normalizedStyleSource, styleSampleSet);
|
|
199
|
-
|
|
200
|
-
const hardcodedColorCount = countPatternMatches(styleSourceText, COLOR_PATTERN);
|
|
201
|
-
const rawSpacingCount = countPatternMatches(styleSourceText, RAW_SPACING_PATTERN);
|
|
202
|
-
const rawRadiusCount = countPatternMatches(styleSourceText, RAW_RADIUS_PATTERN);
|
|
203
|
-
const rawShadowCount = countPatternMatches(styleSourceText, RAW_SHADOW_PATTERN);
|
|
204
|
-
const cssVariableReferenceCount = countPatternMatches(styleSourceText, CSS_VARIABLE_REFERENCE_PATTERN);
|
|
205
|
-
|
|
206
|
-
summary.structuredInspection.inlineTokenBypassSignals.hardcodedColorCount += hardcodedColorCount;
|
|
207
|
-
summary.structuredInspection.inlineTokenBypassSignals.rawSpacingCount += rawSpacingCount;
|
|
208
|
-
summary.structuredInspection.inlineTokenBypassSignals.rawRadiusCount += rawRadiusCount;
|
|
209
|
-
summary.structuredInspection.inlineTokenBypassSignals.rawShadowCount += rawShadowCount;
|
|
210
|
-
summary.structuredInspection.inlineTokenBypassSignals.cssVariableReferenceCount += cssVariableReferenceCount;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
function collectStructuredAttributeEvidence(sourceText, summary, classSampleSet, styleSampleSet, utilityFamilySampleSet) {
|
|
214
|
-
for (const classAttributeMatch of sourceText.matchAll(STRING_CLASS_ATTRIBUTE_PATTERN)) {
|
|
215
|
-
const classValue = getFirstDefinedCapture(classAttributeMatch.slice(1));
|
|
216
|
-
if (!classValue) {
|
|
217
|
-
continue;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
summary.structuredInspection.classAttributeCount += 1;
|
|
221
|
-
pushSampleValue(
|
|
222
|
-
summary.structuredInspection.classAttributeSamples,
|
|
223
|
-
normalizeEvidenceSample(classValue),
|
|
224
|
-
classSampleSet
|
|
225
|
-
);
|
|
226
|
-
collectTailwindUtilityFamilies(
|
|
227
|
-
classValue,
|
|
228
|
-
summary.structuredInspection.utilityFamilyCounts,
|
|
229
|
-
summary.structuredInspection.utilityFamilySamples,
|
|
230
|
-
utilityFamilySampleSet
|
|
231
|
-
);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
for (const expressionClassAttributeMatch of sourceText.matchAll(EXPRESSION_CLASS_ATTRIBUTE_PATTERN)) {
|
|
235
|
-
const expressionValue = getFirstDefinedCapture(expressionClassAttributeMatch.slice(1));
|
|
236
|
-
if (!expressionValue) {
|
|
237
|
-
continue;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
summary.structuredInspection.boundClassExpressionCount += 1;
|
|
241
|
-
pushSampleValue(
|
|
242
|
-
summary.structuredInspection.classAttributeSamples,
|
|
243
|
-
normalizeEvidenceSample(expressionValue),
|
|
244
|
-
classSampleSet
|
|
245
|
-
);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
for (const inlineStyleMatch of sourceText.matchAll(JSX_INLINE_STYLE_PATTERN)) {
|
|
249
|
-
const inlineStyleSource = getFirstDefinedCapture(inlineStyleMatch.slice(1));
|
|
250
|
-
if (!inlineStyleSource) {
|
|
251
|
-
continue;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
summary.structuredInspection.inlineStyleObjectCount += 1;
|
|
255
|
-
applyInlineStyleInspection(inlineStyleSource, summary, styleSampleSet);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
for (const vueInlineStyleMatch of sourceText.matchAll(VUE_INLINE_STYLE_PATTERN)) {
|
|
259
|
-
const inlineStyleSource = getFirstDefinedCapture(vueInlineStyleMatch.slice(1));
|
|
260
|
-
if (!inlineStyleSource) {
|
|
261
|
-
continue;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
summary.structuredInspection.inlineStyleBindingCount += 1;
|
|
265
|
-
applyInlineStyleInspection(inlineStyleSource, summary, styleSampleSet);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
function createDesignEvidenceSummary(scanRootRelativePaths) {
|
|
270
|
-
return {
|
|
271
|
-
summaryVersion: 'v1',
|
|
272
|
-
source: 'lightweight-static-scan',
|
|
273
|
-
scanRootRelativePaths,
|
|
274
|
-
scannedFileCount: 0,
|
|
275
|
-
cssVariables: {
|
|
276
|
-
definitionCount: 0,
|
|
277
|
-
referenceCount: 0,
|
|
278
|
-
sampleNames: [],
|
|
279
|
-
categoryCounts: {
|
|
280
|
-
color: 0,
|
|
281
|
-
spacing: 0,
|
|
282
|
-
radius: 0,
|
|
283
|
-
shadow: 0,
|
|
284
|
-
typography: 0,
|
|
285
|
-
motion: 0,
|
|
286
|
-
other: 0,
|
|
287
|
-
},
|
|
288
|
-
},
|
|
289
|
-
colors: {
|
|
290
|
-
hardcodedCount: 0,
|
|
291
|
-
kindCounts: {
|
|
292
|
-
hex: 0,
|
|
293
|
-
rgb: 0,
|
|
294
|
-
hsl: 0,
|
|
295
|
-
oklch: 0,
|
|
296
|
-
other: 0,
|
|
297
|
-
},
|
|
298
|
-
sampleValues: [],
|
|
299
|
-
},
|
|
300
|
-
spacing: {
|
|
301
|
-
rawValueCount: 0,
|
|
302
|
-
sampleValues: [],
|
|
303
|
-
},
|
|
304
|
-
radius: {
|
|
305
|
-
rawValueCount: 0,
|
|
306
|
-
sampleValues: [],
|
|
307
|
-
},
|
|
308
|
-
shadow: {
|
|
309
|
-
rawValueCount: 0,
|
|
310
|
-
sampleValues: [],
|
|
311
|
-
},
|
|
312
|
-
typography: {
|
|
313
|
-
fontFamilyCount: 0,
|
|
314
|
-
fontSizeCount: 0,
|
|
315
|
-
lineHeightCount: 0,
|
|
316
|
-
letterSpacingCount: 0,
|
|
317
|
-
fontFamilySamples: [],
|
|
318
|
-
fontSizeSamples: [],
|
|
319
|
-
lineHeightSamples: [],
|
|
320
|
-
letterSpacingSamples: [],
|
|
321
|
-
},
|
|
322
|
-
motion: {
|
|
323
|
-
transitionCount: 0,
|
|
324
|
-
animationCount: 0,
|
|
325
|
-
durationCount: 0,
|
|
326
|
-
durationSamples: [],
|
|
327
|
-
},
|
|
328
|
-
tailwind: {
|
|
329
|
-
breakpointUsageCount: 0,
|
|
330
|
-
arbitraryBreakpointCount: 0,
|
|
331
|
-
utilityFamilyCounts: {},
|
|
332
|
-
utilityFamilySamples: [],
|
|
333
|
-
},
|
|
334
|
-
componentInventory: {
|
|
335
|
-
componentFileCount: 0,
|
|
336
|
-
pageFileCount: 0,
|
|
337
|
-
layoutFileCount: 0,
|
|
338
|
-
surfaceFileSamples: [],
|
|
339
|
-
},
|
|
340
|
-
structuredInspection: {
|
|
341
|
-
mode: 'attribute-aware-static-scan',
|
|
342
|
-
classAttributeCount: 0,
|
|
343
|
-
boundClassExpressionCount: 0,
|
|
344
|
-
inlineStyleObjectCount: 0,
|
|
345
|
-
inlineStyleBindingCount: 0,
|
|
346
|
-
classAttributeSamples: [],
|
|
347
|
-
inlineStyleSamples: [],
|
|
348
|
-
utilityFamilyCounts: {},
|
|
349
|
-
utilityFamilySamples: [],
|
|
350
|
-
inlineTokenBypassSignals: {
|
|
351
|
-
hardcodedColorCount: 0,
|
|
352
|
-
rawSpacingCount: 0,
|
|
353
|
-
rawRadiusCount: 0,
|
|
354
|
-
rawShadowCount: 0,
|
|
355
|
-
cssVariableReferenceCount: 0,
|
|
356
|
-
},
|
|
357
|
-
},
|
|
358
|
-
tokenBypassSignals: {
|
|
359
|
-
hardcodedColorCount: 0,
|
|
360
|
-
rawSpacingCount: 0,
|
|
361
|
-
rawRadiusCount: 0,
|
|
362
|
-
rawShadowCount: 0,
|
|
363
|
-
inlineHardcodedColorCount: 0,
|
|
364
|
-
inlineRawSpacingCount: 0,
|
|
365
|
-
inlineRawRadiusCount: 0,
|
|
366
|
-
inlineRawShadowCount: 0,
|
|
367
|
-
inlineCssVariableReferenceCount: 0,
|
|
368
|
-
},
|
|
369
|
-
};
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
function registerSurfaceFile(summary, targetDirectoryPath, scannedFilePath, seenSurfaceFiles) {
|
|
373
|
-
const relativeFilePath = path.relative(targetDirectoryPath, scannedFilePath).replace(/\\/g, '/');
|
|
374
|
-
const normalizedBaseName = path.basename(scannedFilePath, path.extname(scannedFilePath)).toLowerCase();
|
|
375
|
-
const looksLikeComponent = /[A-Z]/.test(path.basename(scannedFilePath, path.extname(scannedFilePath)))
|
|
376
|
-
|| relativeFilePath.includes('/components/')
|
|
377
|
-
|| relativeFilePath.startsWith('components/');
|
|
378
|
-
const looksLikePage = normalizedBaseName === 'page'
|
|
379
|
-
|| normalizedBaseName === 'index'
|
|
380
|
-
|| relativeFilePath.includes('/pages/')
|
|
381
|
-
|| relativeFilePath.startsWith('pages/')
|
|
382
|
-
|| relativeFilePath.includes('/app/');
|
|
383
|
-
const looksLikeLayout = normalizedBaseName === 'layout' || relativeFilePath.includes('/layouts/');
|
|
384
|
-
|
|
385
|
-
if (looksLikeComponent) {
|
|
386
|
-
summary.componentInventory.componentFileCount += 1;
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
if (looksLikePage) {
|
|
390
|
-
summary.componentInventory.pageFileCount += 1;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
if (looksLikeLayout) {
|
|
394
|
-
summary.componentInventory.layoutFileCount += 1;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
if ((looksLikeComponent || looksLikePage || looksLikeLayout) && !seenSurfaceFiles.has(relativeFilePath)) {
|
|
398
|
-
seenSurfaceFiles.add(relativeFilePath);
|
|
399
|
-
if (summary.componentInventory.surfaceFileSamples.length < DESIGN_EVIDENCE_SAMPLE_LIMIT) {
|
|
400
|
-
summary.componentInventory.surfaceFileSamples.push(relativeFilePath);
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
function collectSampleMatches(sourceText, pattern, targetSamples, targetSet, transform = (match) => match[1] || match[0]) {
|
|
406
|
-
for (const match of sourceText.matchAll(pattern)) {
|
|
407
|
-
pushSampleValue(targetSamples, transform(match), targetSet);
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
export async function collectFrontendDesignEvidence({
|
|
412
|
-
targetDirectoryPath,
|
|
413
|
-
markerNames,
|
|
414
|
-
scanRootDirectoryPaths = [],
|
|
415
|
-
}) {
|
|
416
|
-
const candidateDirectoryPaths = FRONTEND_SCAN_DIRECTORY_NAMES
|
|
417
|
-
.filter((directoryName) => markerNames.has(directoryName))
|
|
418
|
-
.map((directoryName) => path.join(targetDirectoryPath, directoryName));
|
|
419
|
-
const explicitScanRootDirectoryPaths = Array.isArray(scanRootDirectoryPaths)
|
|
420
|
-
? scanRootDirectoryPaths.filter((scanRootDirectoryPath) => typeof scanRootDirectoryPath === 'string' && scanRootDirectoryPath.trim().length > 0)
|
|
421
|
-
: [];
|
|
422
|
-
const resolvedCandidateDirectoryPaths = explicitScanRootDirectoryPaths.length > 0
|
|
423
|
-
? Array.from(new Set(explicitScanRootDirectoryPaths))
|
|
424
|
-
: candidateDirectoryPaths.length > 0
|
|
425
|
-
? candidateDirectoryPaths
|
|
426
|
-
: [targetDirectoryPath];
|
|
427
|
-
const scannedFilePaths = [];
|
|
428
|
-
const scanRootRelativePaths = resolvedCandidateDirectoryPaths
|
|
429
|
-
.map((candidateDirectoryPath) => path.relative(targetDirectoryPath, candidateDirectoryPath).replace(/\\/g, '/') || '.');
|
|
430
|
-
const designEvidenceSummary = createDesignEvidenceSummary(scanRootRelativePaths);
|
|
431
|
-
const cssVariableSamples = new Set();
|
|
432
|
-
const colorSamples = new Set();
|
|
433
|
-
const spacingSamples = new Set();
|
|
434
|
-
const radiusSamples = new Set();
|
|
435
|
-
const shadowSamples = new Set();
|
|
436
|
-
const fontFamilySamples = new Set();
|
|
437
|
-
const fontSizeSamples = new Set();
|
|
438
|
-
const lineHeightSamples = new Set();
|
|
439
|
-
const letterSpacingSamples = new Set();
|
|
440
|
-
const durationSamples = new Set();
|
|
441
|
-
const utilityFamilySamples = new Set();
|
|
442
|
-
const structuredClassAttributeSamples = new Set();
|
|
443
|
-
const structuredInlineStyleSamples = new Set();
|
|
444
|
-
const structuredUtilityFamilySamples = new Set();
|
|
445
|
-
const seenSurfaceFiles = new Set();
|
|
446
|
-
let hardcodedColorCount = 0;
|
|
447
|
-
let propDrillingCandidateCount = 0;
|
|
448
|
-
let mediaQueryCount = 0;
|
|
449
|
-
let tailwindBreakpointUsageCount = 0;
|
|
450
|
-
let arbitraryBreakpointCount = 0;
|
|
451
|
-
const uniqueMediaWidths = new Set();
|
|
452
|
-
|
|
453
|
-
for (const candidateDirectoryPath of resolvedCandidateDirectoryPaths) {
|
|
454
|
-
await collectFrontendSourceFilePaths(candidateDirectoryPath, scannedFilePaths);
|
|
455
|
-
if (scannedFilePaths.length >= FRONTEND_FILE_SCAN_LIMIT) {
|
|
456
|
-
break;
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
designEvidenceSummary.scannedFileCount = scannedFilePaths.length;
|
|
461
|
-
|
|
462
|
-
for (const scannedFilePath of scannedFilePaths) {
|
|
463
|
-
let sourceText;
|
|
464
|
-
|
|
465
|
-
try {
|
|
466
|
-
const fileStat = await fs.stat(scannedFilePath);
|
|
467
|
-
if (fileStat.size > FRONTEND_FILE_SIZE_LIMIT_BYTES) {
|
|
468
|
-
continue;
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
sourceText = await fs.readFile(scannedFilePath, 'utf8');
|
|
472
|
-
} catch {
|
|
473
|
-
continue;
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
registerSurfaceFile(designEvidenceSummary, targetDirectoryPath, scannedFilePath, seenSurfaceFiles);
|
|
477
|
-
collectStructuredAttributeEvidence(
|
|
478
|
-
sourceText,
|
|
479
|
-
designEvidenceSummary,
|
|
480
|
-
structuredClassAttributeSamples,
|
|
481
|
-
structuredInlineStyleSamples,
|
|
482
|
-
structuredUtilityFamilySamples
|
|
483
|
-
);
|
|
484
|
-
|
|
485
|
-
for (const cssVariableMatch of sourceText.matchAll(CSS_VARIABLE_DEFINITION_PATTERN)) {
|
|
486
|
-
designEvidenceSummary.cssVariables.definitionCount += 1;
|
|
487
|
-
const variableName = cssVariableMatch[1];
|
|
488
|
-
const categoryKey = categorizeCssVariable(variableName);
|
|
489
|
-
incrementCountMap(designEvidenceSummary.cssVariables.categoryCounts, categoryKey);
|
|
490
|
-
pushSampleValue(designEvidenceSummary.cssVariables.sampleNames, variableName, cssVariableSamples);
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
for (const cssVariableReferenceMatch of sourceText.matchAll(CSS_VARIABLE_REFERENCE_PATTERN)) {
|
|
494
|
-
designEvidenceSummary.cssVariables.referenceCount += 1;
|
|
495
|
-
pushSampleValue(
|
|
496
|
-
designEvidenceSummary.cssVariables.sampleNames,
|
|
497
|
-
cssVariableReferenceMatch[1],
|
|
498
|
-
cssVariableSamples
|
|
499
|
-
);
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
for (const colorMatch of sourceText.matchAll(COLOR_PATTERN)) {
|
|
503
|
-
const colorValue = colorMatch[0];
|
|
504
|
-
hardcodedColorCount += 1;
|
|
505
|
-
designEvidenceSummary.colors.hardcodedCount += 1;
|
|
506
|
-
incrementCountMap(designEvidenceSummary.colors.kindCounts, inferColorKind(colorValue));
|
|
507
|
-
pushSampleValue(designEvidenceSummary.colors.sampleValues, colorValue, colorSamples);
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
propDrillingCandidateCount += countPatternMatches(sourceText, PROP_DRILLING_PATTERN);
|
|
511
|
-
mediaQueryCount += countPatternMatches(sourceText, MEDIA_QUERY_PATTERN);
|
|
512
|
-
tailwindBreakpointUsageCount += countPatternMatches(sourceText, TAILWIND_BREAKPOINT_PATTERN);
|
|
513
|
-
arbitraryBreakpointCount += countPatternMatches(sourceText, ARBITRARY_BREAKPOINT_PATTERN);
|
|
514
|
-
designEvidenceSummary.tailwind.breakpointUsageCount += countPatternMatches(sourceText, TAILWIND_BREAKPOINT_PATTERN);
|
|
515
|
-
designEvidenceSummary.tailwind.arbitraryBreakpointCount += countPatternMatches(sourceText, ARBITRARY_BREAKPOINT_PATTERN);
|
|
516
|
-
|
|
517
|
-
for (const rawSpacingMatch of sourceText.matchAll(RAW_SPACING_PATTERN)) {
|
|
518
|
-
designEvidenceSummary.spacing.rawValueCount += 1;
|
|
519
|
-
pushSampleValue(designEvidenceSummary.spacing.sampleValues, rawSpacingMatch[1], spacingSamples);
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
for (const rawRadiusMatch of sourceText.matchAll(RAW_RADIUS_PATTERN)) {
|
|
523
|
-
designEvidenceSummary.radius.rawValueCount += 1;
|
|
524
|
-
pushSampleValue(designEvidenceSummary.radius.sampleValues, rawRadiusMatch[1], radiusSamples);
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
for (const rawShadowMatch of sourceText.matchAll(RAW_SHADOW_PATTERN)) {
|
|
528
|
-
designEvidenceSummary.shadow.rawValueCount += 1;
|
|
529
|
-
pushSampleValue(designEvidenceSummary.shadow.sampleValues, rawShadowMatch[1], shadowSamples);
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
for (const fontFamilyMatch of sourceText.matchAll(FONT_FAMILY_PATTERN)) {
|
|
533
|
-
designEvidenceSummary.typography.fontFamilyCount += 1;
|
|
534
|
-
pushSampleValue(designEvidenceSummary.typography.fontFamilySamples, fontFamilyMatch[1], fontFamilySamples);
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
for (const fontSizeMatch of sourceText.matchAll(FONT_SIZE_PATTERN)) {
|
|
538
|
-
designEvidenceSummary.typography.fontSizeCount += 1;
|
|
539
|
-
pushSampleValue(designEvidenceSummary.typography.fontSizeSamples, fontSizeMatch[1], fontSizeSamples);
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
for (const lineHeightMatch of sourceText.matchAll(LINE_HEIGHT_PATTERN)) {
|
|
543
|
-
designEvidenceSummary.typography.lineHeightCount += 1;
|
|
544
|
-
pushSampleValue(designEvidenceSummary.typography.lineHeightSamples, lineHeightMatch[1], lineHeightSamples);
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
for (const letterSpacingMatch of sourceText.matchAll(LETTER_SPACING_PATTERN)) {
|
|
548
|
-
designEvidenceSummary.typography.letterSpacingCount += 1;
|
|
549
|
-
pushSampleValue(
|
|
550
|
-
designEvidenceSummary.typography.letterSpacingSamples,
|
|
551
|
-
letterSpacingMatch[1],
|
|
552
|
-
letterSpacingSamples
|
|
553
|
-
);
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
designEvidenceSummary.motion.transitionCount += countPatternMatches(sourceText, TRANSITION_PATTERN);
|
|
557
|
-
designEvidenceSummary.motion.animationCount += countPatternMatches(sourceText, ANIMATION_PATTERN);
|
|
558
|
-
|
|
559
|
-
for (const durationMatch of sourceText.matchAll(DURATION_PATTERN)) {
|
|
560
|
-
designEvidenceSummary.motion.durationCount += 1;
|
|
561
|
-
pushSampleValue(designEvidenceSummary.motion.durationSamples, durationMatch[0], durationSamples);
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
for (const mediaWidthMatch of sourceText.matchAll(MEDIA_WIDTH_PATTERN)) {
|
|
565
|
-
uniqueMediaWidths.add(mediaWidthMatch[1]);
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
designEvidenceSummary.tailwind.utilityFamilyCounts = {
|
|
571
|
-
...designEvidenceSummary.structuredInspection.utilityFamilyCounts,
|
|
572
|
-
};
|
|
573
|
-
designEvidenceSummary.tailwind.utilityFamilySamples = [
|
|
574
|
-
...designEvidenceSummary.structuredInspection.utilityFamilySamples,
|
|
575
|
-
];
|
|
576
|
-
|
|
577
|
-
designEvidenceSummary.tokenBypassSignals = {
|
|
578
|
-
hardcodedColorCount: designEvidenceSummary.colors.hardcodedCount,
|
|
579
|
-
rawSpacingCount: designEvidenceSummary.spacing.rawValueCount,
|
|
580
|
-
rawRadiusCount: designEvidenceSummary.radius.rawValueCount,
|
|
581
|
-
rawShadowCount: designEvidenceSummary.shadow.rawValueCount,
|
|
582
|
-
inlineHardcodedColorCount: designEvidenceSummary.structuredInspection.inlineTokenBypassSignals.hardcodedColorCount,
|
|
583
|
-
inlineRawSpacingCount: designEvidenceSummary.structuredInspection.inlineTokenBypassSignals.rawSpacingCount,
|
|
584
|
-
inlineRawRadiusCount: designEvidenceSummary.structuredInspection.inlineTokenBypassSignals.rawRadiusCount,
|
|
585
|
-
inlineRawShadowCount: designEvidenceSummary.structuredInspection.inlineTokenBypassSignals.rawShadowCount,
|
|
586
|
-
inlineCssVariableReferenceCount: designEvidenceSummary.structuredInspection.inlineTokenBypassSignals.cssVariableReferenceCount,
|
|
587
|
-
};
|
|
588
|
-
|
|
589
|
-
return {
|
|
590
|
-
frontendEvidenceMetrics: {
|
|
591
|
-
scannedFileCount: scannedFilePaths.length,
|
|
592
|
-
hardcodedColorCount,
|
|
593
|
-
propDrillingCandidateCount,
|
|
594
|
-
mediaQueryCount,
|
|
595
|
-
tailwindBreakpointUsageCount,
|
|
596
|
-
arbitraryBreakpointCount,
|
|
597
|
-
uniqueMediaWidthCount: uniqueMediaWidths.size,
|
|
598
|
-
structuredClassAttributeCount: designEvidenceSummary.structuredInspection.classAttributeCount,
|
|
599
|
-
boundClassExpressionCount: designEvidenceSummary.structuredInspection.boundClassExpressionCount,
|
|
600
|
-
inlineStyleObjectCount: designEvidenceSummary.structuredInspection.inlineStyleObjectCount,
|
|
601
|
-
inlineStyleBindingCount: designEvidenceSummary.structuredInspection.inlineStyleBindingCount,
|
|
602
|
-
inlineStyleTokenBypassCount:
|
|
603
|
-
designEvidenceSummary.structuredInspection.inlineTokenBypassSignals.hardcodedColorCount
|
|
604
|
-
+ designEvidenceSummary.structuredInspection.inlineTokenBypassSignals.rawSpacingCount
|
|
605
|
-
+ designEvidenceSummary.structuredInspection.inlineTokenBypassSignals.rawRadiusCount
|
|
606
|
-
+ designEvidenceSummary.structuredInspection.inlineTokenBypassSignals.rawShadowCount,
|
|
607
|
-
},
|
|
608
|
-
designEvidenceSummary,
|
|
609
|
-
};
|
|
610
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Aggregator re-export for the design evidence scan. Implementation lives in
|
|
3
|
+
* lib/cli/detector/design-evidence/* split per concern. Importers continue to
|
|
4
|
+
* import the same public surface (collectFrontendDesignEvidence and
|
|
5
|
+
* FRONTEND_SCAN_IGNORE_DIRECTORY_NAMES) from this module.
|
|
6
|
+
*
|
|
7
|
+
* Public schema produced by collectFrontendDesignEvidence (kept here so static
|
|
8
|
+
* tooling can confirm the expected shape without crawling sub-files):
|
|
9
|
+
*
|
|
10
|
+
* designEvidenceSummary.summaryVersion: 'v1'
|
|
11
|
+
* designEvidenceSummary.source: 'lightweight-static-scan'
|
|
12
|
+
* designEvidenceSummary.cssVariables.{ definitionCount, referenceCount, sampleNames, categoryCounts }
|
|
13
|
+
* designEvidenceSummary.componentInventory.{ componentFileCount, pageFileCount, layoutFileCount, surfaceFileSamples }
|
|
14
|
+
* designEvidenceSummary.structuredInspection.{ classAttributeCount, inlineStyleObjectCount, ... }
|
|
15
|
+
* designEvidenceSummary.tokenBypassSignals.{ hardcodedColorCount, rawSpacingCount, rawRadiusCount, rawShadowCount, ... }
|
|
16
|
+
* designEvidenceSummary.tailwind.{ breakpointUsageCount, arbitraryBreakpointCount, utilityFamilyCounts, ... }
|
|
17
|
+
*
|
|
18
|
+
* frontendEvidenceMetrics.{ scannedFileCount, hardcodedColorCount, propDrillingCandidateCount,
|
|
19
|
+
* mediaQueryCount, tailwindBreakpointUsageCount, arbitraryBreakpointCount,
|
|
20
|
+
* uniqueMediaWidthCount, structuredClassAttributeCount, boundClassExpressionCount,
|
|
21
|
+
* inlineStyleObjectCount, inlineStyleBindingCount, inlineStyleTokenBypassCount }
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
export { FRONTEND_SCAN_IGNORE_DIRECTORY_NAMES } from './design-evidence/constants.mjs';
|
|
25
|
+
export { collectFrontendDesignEvidence } from './design-evidence/collector.mjs';
|