@schemyx/mcp 0.1.0 → 0.1.1

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.
Files changed (103) hide show
  1. package/README.md +76 -2
  2. package/dist/backend-client.d.ts +1 -17
  3. package/dist/backend-client.js +15 -91
  4. package/dist/backend-client.js.map +1 -1
  5. package/dist/client/backend-client.d.ts +17 -0
  6. package/dist/client/backend-client.js +97 -0
  7. package/dist/client/backend-client.js.map +1 -0
  8. package/dist/client/local-theme-source.d.ts +19 -0
  9. package/dist/client/local-theme-source.js +737 -0
  10. package/dist/client/local-theme-source.js.map +1 -0
  11. package/dist/client/mcp-client.d.ts +15 -0
  12. package/dist/client/mcp-client.js +46 -0
  13. package/dist/client/mcp-client.js.map +1 -0
  14. package/dist/codebase-scanner/backend.d.ts +12 -0
  15. package/dist/codebase-scanner/backend.js +814 -0
  16. package/dist/codebase-scanner/backend.js.map +1 -0
  17. package/dist/codebase-scanner/bundle.d.ts +81 -0
  18. package/dist/codebase-scanner/bundle.js +2177 -0
  19. package/dist/codebase-scanner/bundle.js.map +1 -0
  20. package/dist/codebase-scanner/constants.d.ts +26 -0
  21. package/dist/codebase-scanner/constants.js +230 -0
  22. package/dist/codebase-scanner/constants.js.map +1 -0
  23. package/dist/codebase-scanner/extractors.d.ts +187 -0
  24. package/dist/codebase-scanner/extractors.js +2600 -0
  25. package/dist/codebase-scanner/extractors.js.map +1 -0
  26. package/dist/codebase-scanner/files.d.ts +16 -0
  27. package/dist/codebase-scanner/files.js +233 -0
  28. package/dist/codebase-scanner/files.js.map +1 -0
  29. package/dist/codebase-scanner/index.d.ts +217 -0
  30. package/dist/codebase-scanner/index.js +387 -0
  31. package/dist/codebase-scanner/index.js.map +1 -0
  32. package/dist/codebase-scanner/recipes.d.ts +74 -0
  33. package/dist/codebase-scanner/recipes.js +585 -0
  34. package/dist/codebase-scanner/recipes.js.map +1 -0
  35. package/dist/codebase-scanner/storage.d.ts +19 -0
  36. package/dist/codebase-scanner/storage.js +103 -0
  37. package/dist/codebase-scanner/storage.js.map +1 -0
  38. package/dist/codebase-scanner/types.d.ts +522 -0
  39. package/dist/codebase-scanner/types.js +3 -0
  40. package/dist/codebase-scanner/types.js.map +1 -0
  41. package/dist/codebase-scanner/utils.d.ts +37 -0
  42. package/dist/codebase-scanner/utils.js +259 -0
  43. package/dist/codebase-scanner/utils.js.map +1 -0
  44. package/dist/codebase-scanner.d.ts +1 -0
  45. package/dist/codebase-scanner.js +18 -0
  46. package/dist/codebase-scanner.js.map +1 -0
  47. package/dist/config.d.ts +1 -2
  48. package/dist/config.js +15 -37
  49. package/dist/config.js.map +1 -1
  50. package/dist/local-theme-source.d.ts +1 -0
  51. package/dist/local-theme-source.js +18 -0
  52. package/dist/local-theme-source.js.map +1 -0
  53. package/dist/main.js +3 -3
  54. package/dist/main.js.map +1 -1
  55. package/dist/mcp-client.d.ts +1 -0
  56. package/dist/mcp-client.js +18 -0
  57. package/dist/mcp-client.js.map +1 -0
  58. package/dist/prompts.d.ts +1 -7
  59. package/dist/prompts.js +15 -52
  60. package/dist/prompts.js.map +1 -1
  61. package/dist/server/index.d.ts +4 -0
  62. package/dist/server/index.js +163 -0
  63. package/dist/server/index.js.map +1 -0
  64. package/dist/server/prompts.d.ts +7 -0
  65. package/dist/server/prompts.js +55 -0
  66. package/dist/server/prompts.js.map +1 -0
  67. package/dist/server/tool-definitions.d.ts +22 -0
  68. package/dist/server/tool-definitions.js +531 -0
  69. package/dist/server/tool-definitions.js.map +1 -0
  70. package/dist/server.d.ts +3 -3
  71. package/dist/server.js +33 -0
  72. package/dist/server.js.map +1 -1
  73. package/dist/shared/config.d.ts +2 -0
  74. package/dist/shared/config.js +54 -0
  75. package/dist/shared/config.js.map +1 -0
  76. package/dist/shared/text.d.ts +14 -0
  77. package/dist/shared/text.js +33 -0
  78. package/dist/shared/text.js.map +1 -0
  79. package/dist/shared/types.d.ts +118 -0
  80. package/dist/shared/types.js +3 -0
  81. package/dist/shared/types.js.map +1 -0
  82. package/dist/shared/uri.d.ts +6 -0
  83. package/dist/shared/uri.js +24 -0
  84. package/dist/shared/uri.js.map +1 -0
  85. package/dist/style-recipes.d.ts +1 -0
  86. package/dist/style-recipes.js +18 -0
  87. package/dist/style-recipes.js.map +1 -0
  88. package/dist/text.d.ts +1 -14
  89. package/dist/text.js +15 -30
  90. package/dist/text.js.map +1 -1
  91. package/dist/theme/style-recipes.d.ts +26 -0
  92. package/dist/theme/style-recipes.js +129 -0
  93. package/dist/theme/style-recipes.js.map +1 -0
  94. package/dist/tool-definitions.d.ts +1 -11
  95. package/dist/tool-definitions.js +15 -127
  96. package/dist/tool-definitions.js.map +1 -1
  97. package/dist/types.d.ts +1 -106
  98. package/dist/types.js +15 -0
  99. package/dist/types.js.map +1 -1
  100. package/dist/uri.d.ts +1 -6
  101. package/dist/uri.js +15 -21
  102. package/dist/uri.js.map +1 -1
  103. package/package.json +5 -2
@@ -0,0 +1,2600 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.extractImports = extractImports;
4
+ exports.extractExports = extractExports;
5
+ exports.extractSymbols = extractSymbols;
6
+ exports.extractFunctions = extractFunctions;
7
+ exports.extractClasses = extractClasses;
8
+ exports.extractTypes = extractTypes;
9
+ exports.extractConstants = extractConstants;
10
+ exports.extractComponents = extractComponents;
11
+ exports.extractComponentProps = extractComponentProps;
12
+ exports.extractHooks = extractHooks;
13
+ exports.extractJsxElements = extractJsxElements;
14
+ exports.extractRoutes = extractRoutes;
15
+ exports.extractApiHandlers = extractApiHandlers;
16
+ exports.extractDecoratedClasses = extractDecoratedClasses;
17
+ exports.extractControllers = extractControllers;
18
+ exports.extractServices = extractServices;
19
+ exports.extractModules = extractModules;
20
+ exports.extractNamedClasses = extractNamedClasses;
21
+ exports.extractPrismaModels = extractPrismaModels;
22
+ exports.extractSqlModels = extractSqlModels;
23
+ exports.extractOrmModels = extractOrmModels;
24
+ exports.extractModelFields = extractModelFields;
25
+ exports.extractCssVariables = extractCssVariables;
26
+ exports.extractCssVariableValues = extractCssVariableValues;
27
+ exports.extractCssRules = extractCssRules;
28
+ exports.extractCssClasses = extractCssClasses;
29
+ exports.extractCssIds = extractCssIds;
30
+ exports.extractCssVariableReferences = extractCssVariableReferences;
31
+ exports.extractClassReferences = extractClassReferences;
32
+ exports.extractClassExpressions = extractClassExpressions;
33
+ exports.extractComponentStyleDefinitions = extractComponentStyleDefinitions;
34
+ exports.extractThemeTokens = extractThemeTokens;
35
+ exports.extractInlineStyles = extractInlineStyles;
36
+ exports.nearestCssSelector = nearestCssSelector;
37
+ exports.parseCssDeclarations = parseCssDeclarations;
38
+ exports.parseJsStyleObject = parseJsStyleObject;
39
+ exports.readTagBefore = readTagBefore;
40
+ exports.readBalancedCall = readBalancedCall;
41
+ exports.readBalancedBlock = readBalancedBlock;
42
+ exports.readBalancedRange = readBalancedRange;
43
+ exports.skipQuoted = skipQuoted;
44
+ exports.isInsideQuotedString = isInsideQuotedString;
45
+ exports.extractClassListsFromStrings = extractClassListsFromStrings;
46
+ exports.analyzeClassSource = analyzeClassSource;
47
+ exports.toClassSourceAnalysis = toClassSourceAnalysis;
48
+ exports.isConditionalClassLiteral = isConditionalClassLiteral;
49
+ exports.readStringLiteralRanges = readStringLiteralRanges;
50
+ exports.directClassCandidate = directClassCandidate;
51
+ exports.readStringLiterals = readStringLiterals;
52
+ exports.looksLikeClassList = looksLikeClassList;
53
+ exports.isLikelyStyleClass = isLikelyStyleClass;
54
+ exports.firstStringLiteral = firstStringLiteral;
55
+ exports.extractNamedObjectBlock = extractNamedObjectBlock;
56
+ exports.parseVariantGroups = parseVariantGroups;
57
+ exports.parseStringValueMap = parseStringValueMap;
58
+ exports.parseClassValueMap = parseClassValueMap;
59
+ exports.parseTopLevelObjectEntries = parseTopLevelObjectEntries;
60
+ exports.stripOuterBraces = stripOuterBraces;
61
+ exports.skipSeparators = skipSeparators;
62
+ exports.skipWhitespace = skipWhitespace;
63
+ exports.readObjectKey = readObjectKey;
64
+ exports.readObjectValueEnd = readObjectValueEnd;
65
+ exports.splitTopLevel = splitTopLevel;
66
+ exports.cleanupBareValue = cleanupBareValue;
67
+ exports.classifyThemeToken = classifyThemeToken;
68
+ exports.flattenJsonTokens = flattenJsonTokens;
69
+ exports.readOpeningTags = readOpeningTags;
70
+ exports.annotateOpeningTags = annotateOpeningTags;
71
+ exports.findClosingTagEnd = findClosingTagEnd;
72
+ exports.isVoidHtmlElement = isVoidHtmlElement;
73
+ exports.extractElementLabel = extractElementLabel;
74
+ exports.readClassSource = readClassSource;
75
+ exports.readUiProps = readUiProps;
76
+ exports.readVariantProps = readVariantProps;
77
+ exports.readAttrSource = readAttrSource;
78
+ exports.readAttrValueSource = readAttrValueSource;
79
+ exports.cleanupAttrValue = cleanupAttrValue;
80
+ exports.hasBooleanAttr = hasBooleanAttr;
81
+ exports.groupTailwindClasses = groupTailwindClasses;
82
+ exports.addClassGroup = addClassGroup;
83
+ exports.mergeClassGroups = mergeClassGroups;
84
+ exports.mergeVariantProps = mergeVariantProps;
85
+ exports.isCompositionElement = isCompositionElement;
86
+ exports.filterClasses = filterClasses;
87
+ exports.isResponsiveClass = isResponsiveClass;
88
+ exports.isSpacingClass = isSpacingClass;
89
+ exports.isSurfaceClass = isSurfaceClass;
90
+ exports.isDecorativeAccentClass = isDecorativeAccentClass;
91
+ exports.isTypographyClass = isTypographyClass;
92
+ exports.groupClassExpressionsByTarget = groupClassExpressionsByTarget;
93
+ exports.buildComponentSlots = buildComponentSlots;
94
+ exports.createUiCompositionGuidance = createUiCompositionGuidance;
95
+ exports.createUiPatternGuidance = createUiPatternGuidance;
96
+ exports.extractTailwindUtilities = extractTailwindUtilities;
97
+ exports.extractPackageDependencies = extractPackageDependencies;
98
+ exports.extractUiElements = extractUiElements;
99
+ exports.summarizeDirectUiChildren = summarizeDirectUiChildren;
100
+ exports.deriveLayoutRole = deriveLayoutRole;
101
+ exports.isStateClass = isStateClass;
102
+ exports.extractApiCalls = extractApiCalls;
103
+ exports.extractEnvVars = extractEnvVars;
104
+ exports.extractTestNames = extractTestNames;
105
+ exports.extractDocsHeadings = extractDocsHeadings;
106
+ exports.extractConfigKeys = extractConfigKeys;
107
+ exports.extractTemplateIncludes = extractTemplateIncludes;
108
+ exports.extractAuthHints = extractAuthHints;
109
+ exports.extractMiddleware = extractMiddleware;
110
+ exports.extractJobs = extractJobs;
111
+ exports.extractEvents = extractEvents;
112
+ exports.detectKind = detectKind;
113
+ exports.detectLayer = detectLayer;
114
+ exports.detectLanguage = detectLanguage;
115
+ exports.summarizeFile = summarizeFile;
116
+ exports.isNextPageFile = isNextPageFile;
117
+ exports.isNextLayoutFile = isNextLayoutFile;
118
+ exports.isNextRouteFile = isNextRouteFile;
119
+ exports.isPagesRouterPageFile = isPagesRouterPageFile;
120
+ exports.isSvelteKitPageFile = isSvelteKitPageFile;
121
+ exports.isAstroPageFile = isAstroPageFile;
122
+ exports.isNuxtPageFile = isNuxtPageFile;
123
+ exports.isRawPageFile = isRawPageFile;
124
+ exports.toNextRoutePath = toNextRoutePath;
125
+ exports.toPagesRoutePath = toPagesRoutePath;
126
+ exports.toSvelteKitRoutePath = toSvelteKitRoutePath;
127
+ exports.toAstroRoutePath = toAstroRoutePath;
128
+ exports.toNuxtRoutePath = toNuxtRoutePath;
129
+ exports.toRawPageRoutePath = toRawPageRoutePath;
130
+ exports.extractFrameworkRoutePaths = extractFrameworkRoutePaths;
131
+ exports.normalizeRoutePath = normalizeRoutePath;
132
+ exports.shouldIgnoreDirectory = shouldIgnoreDirectory;
133
+ exports.isExcluded = isExcluded;
134
+ exports.shouldIgnoreNonCanonicalPath = shouldIgnoreNonCanonicalPath;
135
+ exports.isScannableFile = isScannableFile;
136
+ exports.shouldIgnoreFileName = shouldIgnoreFileName;
137
+ exports.isUiFile = isUiFile;
138
+ exports.isTemplateFile = isTemplateFile;
139
+ exports.isTestFile = isTestFile;
140
+ exports.isConfigFile = isConfigFile;
141
+ exports.routeExtractorForFile = routeExtractorForFile;
142
+ exports.routeConfidenceForFile = routeConfidenceForFile;
143
+ exports.apiExtractorForFile = apiExtractorForFile;
144
+ exports.controllerExtractorForFile = controllerExtractorForFile;
145
+ exports.serviceExtractorForFile = serviceExtractorForFile;
146
+ exports.moduleExtractorForFile = moduleExtractorForFile;
147
+ exports.modelExtractorForFile = modelExtractorForFile;
148
+ exports.styleExtractorForFile = styleExtractorForFile;
149
+ exports.styleConfidenceForFile = styleConfidenceForFile;
150
+ exports.entryMatchesConcept = entryMatchesConcept;
151
+ exports.splitClassList = splitClassList;
152
+ exports.classifyUiElement = classifyUiElement;
153
+ exports.stripTags = stripTags;
154
+ const path = require("node:path");
155
+ const constants_1 = require("./constants");
156
+ const utils_1 = require("./utils");
157
+ function extractImports(content) {
158
+ const imports = new Set();
159
+ const importPattern = /import(?:\s+type)?[\s\S]*?\sfrom\s+['"]([^'"]+)['"]/g;
160
+ const sideEffectPattern = /import\s+['"]([^'"]+)['"]/g;
161
+ const requirePattern = /require\(\s*['"]([^'"]+)['"]\s*\)/g;
162
+ const cssImportPattern = /@import\s+(?:url\()?['"]?([^'");]+)['"]?\)?/g;
163
+ const phpIncludePattern = /\b(?:require|require_once|include|include_once)\s*(?:\(?\s*)['"]([^'"]+)['"]/g;
164
+ const phpUsePattern = /^\s*use\s+([A-Za-z_\\][A-Za-z0-9_\\]*)\s*;/gm;
165
+ const pythonImportPattern = /^\s*(?:from\s+([A-Za-z_][\w.]*)(?:\s+import\s+[\w*,\s]+)|import\s+([A-Za-z_][\w.]*))/gm;
166
+ const rubyRequirePattern = /\brequire(?:_relative)?\s+['"]([^'"]+)['"]/g;
167
+ const javaLikeImportPattern = /^\s*import\s+(?:static\s+)?([A-Za-z_][\w.*]*);/gm;
168
+ const goImportPattern = /^\s*import\s+(?:\(\s*([\s\S]*?)\s*\)|"([^"]+)")/gm;
169
+ for (const pattern of [
170
+ importPattern,
171
+ sideEffectPattern,
172
+ requirePattern,
173
+ cssImportPattern,
174
+ phpIncludePattern,
175
+ phpUsePattern,
176
+ pythonImportPattern,
177
+ rubyRequirePattern,
178
+ javaLikeImportPattern,
179
+ goImportPattern,
180
+ ]) {
181
+ for (const match of content.matchAll(pattern)) {
182
+ if (pattern === goImportPattern && match[1]) {
183
+ for (const importMatch of match[1].matchAll(/"([^"]+)"/g)) {
184
+ imports.add(importMatch[1]);
185
+ }
186
+ continue;
187
+ }
188
+ imports.add(match[1] || match[2]);
189
+ }
190
+ }
191
+ return Array.from(imports).filter(Boolean).sort();
192
+ }
193
+ function extractExports(content) {
194
+ const exports = new Set();
195
+ const patterns = [
196
+ /export\s+(?:async\s+)?function\s+([A-Za-z_$][\w$]*)/g,
197
+ /export\s+(?:const|let|var)\s+([A-Za-z_$][\w$]*)/g,
198
+ /export\s+class\s+([A-Za-z_$][\w$]*)/g,
199
+ /export\s+default\s+(?:async\s+)?function\s+([A-Za-z_$][\w$]*)?/g,
200
+ /module\.exports\s*=\s*([A-Za-z_$][\w$]*)/g,
201
+ /exports\.([A-Za-z_$][\w$]*)\s*=/g,
202
+ ];
203
+ for (const pattern of patterns) {
204
+ for (const match of content.matchAll(pattern)) {
205
+ exports.add(match[1] || 'default');
206
+ }
207
+ }
208
+ return Array.from(exports).sort();
209
+ }
210
+ function extractSymbols(content, relPath) {
211
+ return (0, utils_1.unique)([
212
+ ...extractExports(content),
213
+ ...extractFunctions(content, relPath),
214
+ ...extractClasses(content, relPath),
215
+ ...extractTypes(content, relPath),
216
+ ...extractConstants(content, relPath),
217
+ ]).sort();
218
+ }
219
+ function extractFunctions(content, relPath) {
220
+ const names = new Set();
221
+ const language = detectLanguage(relPath);
222
+ const patterns = [
223
+ /(?:export\s+)?(?:async\s+)?function\s+([A-Za-z_$][\w$]*)\s*\(/g,
224
+ /(?:export\s+)?(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:async\s*)?\(?[^=]*?\)?\s*=>/g,
225
+ /function\s+([A-Za-z_][\w]*)\s*\(/g,
226
+ /def\s+([A-Za-z_][\w]*)\s*\(/g,
227
+ /func\s+(?:\([^)]+\)\s*)?([A-Za-z_][\w]*)\s*\(/g,
228
+ ];
229
+ for (const pattern of patterns) {
230
+ for (const match of content.matchAll(pattern)) {
231
+ names.add(match[1]);
232
+ }
233
+ }
234
+ if (language === 'rb') {
235
+ for (const match of content.matchAll(/^\s*def\s+([A-Za-z_][\w!?=]*)/gm)) {
236
+ names.add(match[1]);
237
+ }
238
+ }
239
+ return Array.from(names).sort();
240
+ }
241
+ function extractClasses(content, relPath) {
242
+ const names = new Set();
243
+ for (const pattern of [
244
+ /(?:export\s+)?class\s+([A-Za-z_$][\w$]*)/g,
245
+ /(?:class|interface|enum)\s+([A-Z][A-Za-z0-9_]*)/g,
246
+ /class\s+([A-Za-z_][\w]*)\s*(?:\(|:|<|{|extends)?/g,
247
+ /module\s+([A-Z][A-Za-z0-9_:]*)/g,
248
+ ]) {
249
+ for (const match of content.matchAll(pattern)) {
250
+ names.add(match[1]);
251
+ }
252
+ }
253
+ if (/\.(java|kt|kts|cs)$/.test(relPath)) {
254
+ for (const match of content.matchAll(/\b(?:class|interface|record|enum|object)\s+([A-Z][A-Za-z0-9_]*)/g)) {
255
+ names.add(match[1]);
256
+ }
257
+ }
258
+ return Array.from(names).sort();
259
+ }
260
+ function extractTypes(content, relPath) {
261
+ const names = new Set();
262
+ for (const pattern of [
263
+ /(?:export\s+)?(?:type|interface|enum)\s+([A-Za-z_$][\w$]*)/g,
264
+ /type\s+([A-Za-z_][\w]*)\s+(?:struct|interface)/g,
265
+ ]) {
266
+ for (const match of content.matchAll(pattern)) {
267
+ names.add(match[1]);
268
+ }
269
+ }
270
+ if (/\.graphql$/.test(relPath)) {
271
+ for (const match of content.matchAll(/\b(?:type|interface|enum|input)\s+([A-Za-z_][\w]*)/g)) {
272
+ names.add(match[1]);
273
+ }
274
+ }
275
+ return Array.from(names).sort();
276
+ }
277
+ function extractConstants(content, relPath) {
278
+ const names = new Set();
279
+ for (const pattern of [
280
+ /(?:export\s+)?(?:const|let|var)\s+([A-Z][A-Z0-9_]*)\b/g,
281
+ /^\s*([A-Z][A-Z0-9_]+)\s*=/gm,
282
+ ]) {
283
+ for (const match of content.matchAll(pattern)) {
284
+ names.add(match[1]);
285
+ }
286
+ }
287
+ if (/\.(go|rs)$/.test(relPath)) {
288
+ for (const match of content.matchAll(/\bconst\s+([A-Za-z_][\w]*)/g)) {
289
+ names.add(match[1]);
290
+ }
291
+ }
292
+ return Array.from(names).sort();
293
+ }
294
+ function extractComponents(content, filePath, relPath) {
295
+ const names = new Set();
296
+ const extension = path.extname(filePath);
297
+ if (!isUiFile(relPath)) {
298
+ return [];
299
+ }
300
+ for (const exported of extractExports(content)) {
301
+ if ((0, utils_1.isPascalCase)(exported)) {
302
+ names.add(exported);
303
+ }
304
+ }
305
+ for (const pattern of [
306
+ /function\s+([A-Z][A-Za-z0-9_]*)\s*\(/g,
307
+ /const\s+([A-Z][A-Za-z0-9_]*)\s*=\s*(?:\([^)]*\)|[A-Za-z_$][\w$]*)\s*=>/g,
308
+ /name\s*:\s*['"]([A-Z][A-Za-z0-9_]*)['"]/g,
309
+ ]) {
310
+ for (const match of content.matchAll(pattern)) {
311
+ names.add(match[1]);
312
+ }
313
+ }
314
+ if (['.vue', '.svelte', '.astro'].includes(extension) || isTemplateFile(relPath)) {
315
+ const fileStem = (0, utils_1.toPascalCase)(path.basename(relPath).replace(/\.[^.]+(?:\.[^.]+)?$/, ''));
316
+ if (fileStem && !['Index', 'Page', 'Layout'].includes(fileStem)) {
317
+ names.add(fileStem);
318
+ }
319
+ }
320
+ return Array.from(names).sort();
321
+ }
322
+ function extractComponentProps(content, components) {
323
+ const propsByComponent = {};
324
+ for (const component of components) {
325
+ const props = new Set();
326
+ const paramPattern = new RegExp(`(?:function\\s+${(0, utils_1.escapeRegExp)(component)}|const\\s+${(0, utils_1.escapeRegExp)(component)}\\s*=)\\s*\\(?\\s*\\{([^}]+)\\}`);
327
+ const paramMatch = content.match(paramPattern);
328
+ if (paramMatch) {
329
+ for (const prop of paramMatch[1]
330
+ .split(',')
331
+ .map((item) => item.trim().replace(/[:=].*$/, ''))) {
332
+ if (/^[A-Za-z_$][\w$]*$/.test(prop)) {
333
+ props.add(prop);
334
+ }
335
+ }
336
+ }
337
+ const typePattern = new RegExp(`(?:type|interface)\\s+${(0, utils_1.escapeRegExp)(component)}Props\\b[\\s\\S]*?[{=]([\\s\\S]*?)[};]`, 'm');
338
+ const typeMatch = content.match(typePattern);
339
+ if (typeMatch) {
340
+ for (const propMatch of typeMatch[1].matchAll(/([A-Za-z_$][\w$]*)\??\s*:/g)) {
341
+ props.add(propMatch[1]);
342
+ }
343
+ }
344
+ propsByComponent[component] = Array.from(props).sort();
345
+ }
346
+ return propsByComponent;
347
+ }
348
+ function extractHooks(content) {
349
+ const hooks = new Set();
350
+ for (const pattern of [
351
+ /export\s+function\s+(use[A-Z][A-Za-z0-9_]*)\s*\(/g,
352
+ /export\s+const\s+(use[A-Z][A-Za-z0-9_]*)\s*=/g,
353
+ /function\s+(use[A-Z][A-Za-z0-9_]*)\s*\(/g,
354
+ /const\s+(use[A-Z][A-Za-z0-9_]*)\s*=/g,
355
+ ]) {
356
+ for (const match of content.matchAll(pattern)) {
357
+ hooks.add(match[1]);
358
+ }
359
+ }
360
+ return Array.from(hooks).sort();
361
+ }
362
+ function extractJsxElements(content) {
363
+ const elements = new Set();
364
+ for (const match of content.matchAll(/<([A-Z][A-Za-z0-9_.]*)\b/g)) {
365
+ elements.add(match[1].split('.')[0]);
366
+ }
367
+ return Array.from(elements).sort();
368
+ }
369
+ function extractRoutes(relPath, content) {
370
+ const routes = new Set();
371
+ if (isNextPageFile(relPath) || isNextLayoutFile(relPath)) {
372
+ routes.add(toNextRoutePath(relPath));
373
+ }
374
+ if (isPagesRouterPageFile(relPath)) {
375
+ routes.add(toPagesRoutePath(relPath));
376
+ }
377
+ if (isSvelteKitPageFile(relPath)) {
378
+ routes.add(toSvelteKitRoutePath(relPath));
379
+ }
380
+ if (isAstroPageFile(relPath)) {
381
+ routes.add(toAstroRoutePath(relPath));
382
+ }
383
+ if (isNuxtPageFile(relPath)) {
384
+ routes.add(toNuxtRoutePath(relPath));
385
+ }
386
+ if (isRawPageFile(relPath)) {
387
+ routes.add(toRawPageRoutePath(relPath));
388
+ }
389
+ for (const route of extractFrameworkRoutePaths(relPath, content)) {
390
+ routes.add(route);
391
+ }
392
+ return Array.from(routes).sort();
393
+ }
394
+ function extractApiHandlers(relPath, content) {
395
+ const handlers = new Set();
396
+ const routePath = isNextRouteFile(relPath) ? toNextRoutePath(relPath) : null;
397
+ if (routePath) {
398
+ for (const method of ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS', 'HEAD']) {
399
+ if (new RegExp(`export\\s+(?:async\\s+)?(?:function\\s+${method}|const\\s+${method})\\b`).test(content)) {
400
+ handlers.add(`${method} ${routePath}`);
401
+ }
402
+ }
403
+ }
404
+ for (const match of content.matchAll(/\b(?:router|app|server|route)\.(get|post|put|patch|delete|options|head)\(\s*['"`]([^'"`]+)/g)) {
405
+ handlers.add(`${match[1].toUpperCase()} ${match[2]}`);
406
+ }
407
+ for (const match of content.matchAll(/\b(?:fastify|hono|fiber|mux)\.(get|post|put|patch|delete|options|head)\(\s*['"`]([^'"`]+)/gi)) {
408
+ handlers.add(`${match[1].toUpperCase()} ${match[2]}`);
409
+ }
410
+ for (const match of content.matchAll(/@(Get|Post|Put|Patch|Delete)\(\s*['"`]?([^'"`)]*)/g)) {
411
+ handlers.add(`${match[1].toUpperCase()} /${match[2]}`.replace(/\/+$/, '/'));
412
+ }
413
+ for (const match of content.matchAll(/Route::(get|post|put|patch|delete|options|any)\(\s*['"`]([^'"`]+)/gi)) {
414
+ handlers.add(`${match[1].toUpperCase()} ${normalizeRoutePath(match[2])}`);
415
+ }
416
+ for (const match of content.matchAll(/^\s*(get|post|put|patch|delete|resources?)\s+['"]([^'"]+)/gim)) {
417
+ const method = match[1].toLowerCase() === 'resources' ? 'RESOURCE' : match[1].toUpperCase();
418
+ handlers.add(`${method} ${normalizeRoutePath(match[2])}`);
419
+ }
420
+ for (const match of content.matchAll(/@(app|router|api)\.(get|post|put|patch|delete|route)\(\s*['"]([^'"]+)/gi)) {
421
+ const method = match[2].toLowerCase() === 'route' ? 'ANY' : match[2].toUpperCase();
422
+ handlers.add(`${method} ${normalizeRoutePath(match[3])}`);
423
+ }
424
+ for (const match of content.matchAll(/\bpath\(\s*['"]([^'"]+)['"]/g)) {
425
+ handlers.add(`ANY ${normalizeRoutePath(match[1])}`);
426
+ }
427
+ for (const match of content.matchAll(/\burl\(r?['"]\^?([^'"]+)['"]/g)) {
428
+ handlers.add(`ANY ${normalizeRoutePath(match[1].replace(/\$$/, ''))}`);
429
+ }
430
+ return Array.from(handlers).sort();
431
+ }
432
+ function extractDecoratedClasses(content, decoratorName) {
433
+ const names = new Set();
434
+ const pattern = new RegExp(`@${decoratorName}\\([^)]*\\)\\s*(?:export\\s+)?class\\s+([A-Za-z_$][\\w$]*)`, 'g');
435
+ for (const match of content.matchAll(pattern)) {
436
+ names.add(match[1]);
437
+ }
438
+ return Array.from(names).sort();
439
+ }
440
+ function extractControllers(content, relPath) {
441
+ const names = new Set();
442
+ for (const className of extractClasses(content, relPath)) {
443
+ if (/Controller$/.test(className) || /controllers?\//i.test(relPath)) {
444
+ names.add(className);
445
+ }
446
+ }
447
+ return Array.from(names).sort();
448
+ }
449
+ function extractServices(content, relPath) {
450
+ return (0, utils_1.unique)([
451
+ ...extractNamedClasses(content, /Service$/),
452
+ ...extractClasses(content, relPath).filter((name) => /(Service|Repository|Client|Gateway|Provider|Manager)$/.test(name)),
453
+ ]).sort();
454
+ }
455
+ function extractModules(content, relPath) {
456
+ return (0, utils_1.unique)([
457
+ ...extractNamedClasses(content, /Module$/),
458
+ ...extractClasses(content, relPath).filter((name) => /(Module|Package|Bundle)$/.test(name)),
459
+ ]).sort();
460
+ }
461
+ function extractNamedClasses(content, suffix) {
462
+ const names = new Set();
463
+ for (const match of content.matchAll(/(?:export\s+)?class\s+([A-Za-z_$][\w$]*)/g)) {
464
+ if (suffix.test(match[1])) {
465
+ names.add(match[1]);
466
+ }
467
+ }
468
+ return Array.from(names).sort();
469
+ }
470
+ function extractPrismaModels(content) {
471
+ const models = new Set();
472
+ for (const match of content.matchAll(/^model\s+([A-Za-z_$][\w$]*)/gm)) {
473
+ models.add(match[1]);
474
+ }
475
+ return Array.from(models).sort();
476
+ }
477
+ function extractSqlModels(content) {
478
+ const models = new Set();
479
+ for (const match of content.matchAll(/\bCREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?[`"[]?([A-Za-z_][\w.]*)[`"\]]?/gi)) {
480
+ models.add(match[1].split('.').pop() ?? match[1]);
481
+ }
482
+ return Array.from(models).sort();
483
+ }
484
+ function extractOrmModels(content, relPath) {
485
+ const models = new Set();
486
+ for (const match of content.matchAll(/@Entity\([^)]*\)\s*(?:export\s+)?class\s+([A-Za-z_$][\w$]*)/g)) {
487
+ models.add(match[1]);
488
+ }
489
+ for (const match of content.matchAll(/mongoose\.model\(\s*['"]([^'"]+)['"]/g)) {
490
+ models.add(match[1]);
491
+ }
492
+ for (const match of content.matchAll(/\b(?:sequelize|db)\.define\(\s*['"]([^'"]+)['"]/g)) {
493
+ models.add((0, utils_1.toPascalCase)(match[1]));
494
+ }
495
+ for (const match of content.matchAll(/class\s+([A-Z][A-Za-z0-9_]*)\s*<\s*(?:ApplicationRecord|ActiveRecord::Base|Model)\b/g)) {
496
+ models.add(match[1]);
497
+ }
498
+ for (const match of content.matchAll(/class\s+([A-Z][A-Za-z0-9_]*)\s*\([^)]*models\.Model[^)]*\)/g)) {
499
+ models.add(match[1]);
500
+ }
501
+ for (const match of content.matchAll(/class\s+([A-Z][A-Za-z0-9_]*)\s+extends\s+Model\b/g)) {
502
+ models.add(match[1]);
503
+ }
504
+ if (/models?\//.test(relPath) || /\/models?\./.test(relPath)) {
505
+ for (const className of extractClasses(content, relPath)) {
506
+ if ((0, utils_1.isPascalCase)(className)) {
507
+ models.add(className);
508
+ }
509
+ }
510
+ }
511
+ return Array.from(models).sort();
512
+ }
513
+ function extractModelFields(content, relPath) {
514
+ const fieldsByModel = {};
515
+ for (const match of content.matchAll(/^model\s+([A-Za-z_$][\w$]*)\s*\{([\s\S]*?)^}/gm)) {
516
+ fieldsByModel[match[1]] = match[2]
517
+ .split('\n')
518
+ .map((line) => line.trim().match(/^([A-Za-z_$][\w$]*)\s+/)?.[1])
519
+ .filter((field) => Boolean(field))
520
+ .slice(0, 80);
521
+ }
522
+ for (const match of content.matchAll(/\bCREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?[`"[]?([A-Za-z_][\w.]*)[`"\]]?\s*\(([\s\S]*?)\);/gi)) {
523
+ const modelName = match[1].split('.').pop() ?? match[1];
524
+ fieldsByModel[modelName] = match[2]
525
+ .split(',')
526
+ .map((line) => line.trim().match(/^[`"[]?([A-Za-z_][\w]*)[`"\]]?\s+/)?.[1] ?? '')
527
+ .filter((field) => Boolean(field) && !/^(PRIMARY|FOREIGN|UNIQUE|KEY|CONSTRAINT)$/i.test(field))
528
+ .slice(0, 80);
529
+ }
530
+ for (const model of extractOrmModels(content, relPath)) {
531
+ const fields = new Set();
532
+ for (const match of content.matchAll(/(?:this\.|self\.|attr_accessor\s+:|property\s+:|field\s+:)([A-Za-z_][\w]*)/g)) {
533
+ fields.add(match[1]);
534
+ }
535
+ for (const match of content.matchAll(/([A-Za-z_][\w]*)\s*=\s*(?:models\.|Column\(|db\.Column|fields\.)/g)) {
536
+ fields.add(match[1]);
537
+ }
538
+ if (fields.size) {
539
+ fieldsByModel[model] = Array.from(fields).sort().slice(0, 80);
540
+ }
541
+ }
542
+ return fieldsByModel;
543
+ }
544
+ function extractCssVariables(content) {
545
+ return (0, utils_1.unique)(Array.from(content.matchAll(/--[a-zA-Z0-9-_]+/g)).map((match) => match[0])).slice(0, 80);
546
+ }
547
+ function extractCssVariableValues(content) {
548
+ const values = [];
549
+ for (const match of content.matchAll(/(--[a-zA-Z0-9-_]+)\s*:\s*([^;{}]+);/g)) {
550
+ const selector = nearestCssSelector(content, match.index ?? 0);
551
+ values.push({
552
+ name: match[1],
553
+ value: match[2].trim(),
554
+ ...(selector ? { selector } : {}),
555
+ line: (0, utils_1.lineNumberAt)(content, match.index ?? 0),
556
+ });
557
+ }
558
+ return (0, utils_1.uniqueBy)(values, (value) => `${value.name}:${value.value}:${value.selector ?? ''}`).slice(0, 240);
559
+ }
560
+ function extractCssRules(content, relPath) {
561
+ if (!/\.(css|scss|vue|svelte|astro|html|htm|php|phtml|twig|erb)$/.test(relPath)) {
562
+ return [];
563
+ }
564
+ const rules = [];
565
+ for (const match of content.matchAll(/([^{}@][^{}]{0,240})\{([^{}]{1,2400})\}/g)) {
566
+ const selector = match[1].trim().replace(/\s+/g, ' ');
567
+ if (!selector || selector.includes(';') || selector.startsWith('import')) {
568
+ continue;
569
+ }
570
+ const declarations = parseCssDeclarations(match[2]).slice(0, 40);
571
+ if (!declarations.length) {
572
+ continue;
573
+ }
574
+ rules.push({
575
+ selector,
576
+ declarations,
577
+ line: (0, utils_1.lineNumberAt)(content, match.index ?? 0),
578
+ });
579
+ }
580
+ return (0, utils_1.uniqueBy)(rules, (rule) => `${rule.selector}:${rule.line}`).slice(0, 200);
581
+ }
582
+ function extractCssClasses(content) {
583
+ return (0, utils_1.unique)(Array.from(content.matchAll(/\.([a-zA-Z][a-zA-Z0-9_-]+)\b/g)).map((match) => match[1])).slice(0, 80);
584
+ }
585
+ function extractCssIds(content) {
586
+ return (0, utils_1.unique)(Array.from(content.matchAll(/#([a-zA-Z][a-zA-Z0-9_-]+)\b/g)).map((match) => match[1])).slice(0, 80);
587
+ }
588
+ function extractCssVariableReferences(content) {
589
+ return (0, utils_1.unique)(Array.from(content.matchAll(/var\(\s*(--[a-zA-Z0-9-_]+)/g)).map((match) => match[1])).slice(0, 80);
590
+ }
591
+ function extractClassReferences(content, classExpressions = extractClassExpressions(content)) {
592
+ const classes = new Set(classExpressions.flatMap((expression) => expression.classes));
593
+ const patterns = [
594
+ /\bclass(?:Name)?\s*=\s*["'`]([^"'`]+)["'`]/g,
595
+ /\bclass(?:Name)?\s*=\s*\{\s*["'`]([^"'`]+)["'`]\s*\}/g,
596
+ /\bclass:\s*["'`]([^"'`]+)["'`]/g,
597
+ /\bclassList\.(?:add|remove|toggle)\(([^)]*)\)/g,
598
+ ];
599
+ for (const pattern of patterns) {
600
+ for (const match of content.matchAll(pattern)) {
601
+ for (const className of splitClassList(match[1])) {
602
+ classes.add(className);
603
+ }
604
+ }
605
+ }
606
+ return Array.from(classes).sort().slice(0, 500);
607
+ }
608
+ function extractClassExpressions(content) {
609
+ const expressions = [];
610
+ for (const pattern of [
611
+ /\bclass(?:Name)?\s*=\s*(["'`])([\s\S]*?)\1/g,
612
+ /\bclass(?:Name)?\s*=\s*\{\s*(["'`])([\s\S]*?)\1\s*\}/g,
613
+ /\bclass:\s*(["'`])([\s\S]*?)\1/g,
614
+ ]) {
615
+ for (const match of content.matchAll(pattern)) {
616
+ const raw = match[0];
617
+ const classValue = match[2] ?? match[1] ?? '';
618
+ const classes = splitClassList(classValue);
619
+ if (classes.length) {
620
+ const analysis = analyzeClassSource(classValue);
621
+ expressions.push({
622
+ kind: 'attribute',
623
+ raw: (0, utils_1.truncate)(raw, 500),
624
+ classes,
625
+ defaultClasses: analysis.defaultClasses,
626
+ conditionalClasses: analysis.conditionalClasses,
627
+ line: (0, utils_1.lineNumberAt)(content, match.index ?? 0),
628
+ target: readTagBefore(content, match.index ?? 0),
629
+ });
630
+ }
631
+ }
632
+ }
633
+ for (const helperName of ['cn', 'clsx', 'classNames', 'twMerge', 'cva']) {
634
+ const helperPattern = new RegExp(`\\b${helperName}\\s*\\(`, 'g');
635
+ for (const match of content.matchAll(helperPattern)) {
636
+ if (isInsideQuotedString(content, match.index ?? 0)) {
637
+ continue;
638
+ }
639
+ const call = readBalancedCall(content, match.index ?? 0);
640
+ if (!call) {
641
+ continue;
642
+ }
643
+ const analysis = analyzeClassSource(call);
644
+ const classes = analysis.classes;
645
+ if (classes.length) {
646
+ expressions.push({
647
+ kind: helperName === 'cva' ? 'variant-definition' : 'helper',
648
+ raw: (0, utils_1.truncate)(call, 900),
649
+ classes,
650
+ defaultClasses: analysis.defaultClasses,
651
+ conditionalClasses: analysis.conditionalClasses,
652
+ line: (0, utils_1.lineNumberAt)(content, match.index ?? 0),
653
+ target: helperName,
654
+ });
655
+ }
656
+ }
657
+ }
658
+ for (const match of content.matchAll(/(["'`])([^"'`]{5,900})\1/g)) {
659
+ const classes = splitClassList(match[2]);
660
+ if (classes.length >= 2 && looksLikeClassList(classes)) {
661
+ expressions.push({
662
+ kind: 'string-literal',
663
+ raw: (0, utils_1.truncate)(match[0], 500),
664
+ classes,
665
+ defaultClasses: classes,
666
+ conditionalClasses: [],
667
+ line: (0, utils_1.lineNumberAt)(content, match.index ?? 0),
668
+ });
669
+ }
670
+ }
671
+ return (0, utils_1.uniqueBy)(expressions, (expression) => `${expression.kind}:${expression.line}:${expression.raw}`).slice(0, 240);
672
+ }
673
+ function extractComponentStyleDefinitions(content) {
674
+ const definitions = [];
675
+ const cvaPattern = /(?:export\s+)?(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*cva\s*\(/g;
676
+ for (const match of content.matchAll(cvaPattern)) {
677
+ if (isInsideQuotedString(content, match.index ?? 0)) {
678
+ continue;
679
+ }
680
+ const call = readBalancedCall(content, (match.index ?? 0) + match[0].lastIndexOf('cva'));
681
+ if (!call) {
682
+ continue;
683
+ }
684
+ const baseClasses = splitClassList(firstStringLiteral(call) ?? '');
685
+ const variantsBlock = extractNamedObjectBlock(call, 'variants');
686
+ const defaultVariantsBlock = extractNamedObjectBlock(call, 'defaultVariants');
687
+ const variants = variantsBlock ? parseVariantGroups(variantsBlock) : {};
688
+ const defaultVariants = defaultVariantsBlock ? parseStringValueMap(defaultVariantsBlock) : {};
689
+ const classes = (0, utils_1.unique)([
690
+ ...baseClasses,
691
+ ...Object.values(variants).flatMap((variant) => Object.values(variant).flat()),
692
+ ]);
693
+ definitions.push({
694
+ name: match[1],
695
+ kind: 'cva',
696
+ line: (0, utils_1.lineNumberAt)(content, match.index ?? 0),
697
+ baseClasses,
698
+ variants,
699
+ defaultVariants,
700
+ classes,
701
+ source: (0, utils_1.truncate)(call, 2200),
702
+ });
703
+ }
704
+ for (const match of content.matchAll(/(?:export\s+)?(?:const|let|var)\s+([A-Za-z_$][\w$]*(?:Classes|ClassNames|Styles|Variants|Config|Map))\s*=\s*\{/g)) {
705
+ if (isInsideQuotedString(content, match.index ?? 0)) {
706
+ continue;
707
+ }
708
+ const block = readBalancedBlock(content, (match.index ?? 0) + match[0].lastIndexOf('{'));
709
+ if (!block) {
710
+ continue;
711
+ }
712
+ const variants = { values: parseClassValueMap(block) };
713
+ const classes = (0, utils_1.unique)(Object.values(variants.values).flat());
714
+ if (!classes.length) {
715
+ continue;
716
+ }
717
+ definitions.push({
718
+ name: match[1],
719
+ kind: /variant/i.test(match[1]) ? 'variant-object' : 'class-map',
720
+ line: (0, utils_1.lineNumberAt)(content, match.index ?? 0),
721
+ baseClasses: [],
722
+ variants,
723
+ defaultVariants: {},
724
+ classes,
725
+ source: (0, utils_1.truncate)(block, 2200),
726
+ });
727
+ }
728
+ return (0, utils_1.uniqueBy)(definitions, (definition) => `${definition.name}:${definition.line}`).slice(0, 80);
729
+ }
730
+ function extractThemeTokens(relPath, content) {
731
+ const shouldExtract = /(?:theme|token|tailwind|config|globals|styles?|css|scss)/i.test(relPath) ||
732
+ /--[a-zA-Z0-9-_]+\s*:/.test(content);
733
+ if (!shouldExtract) {
734
+ return [];
735
+ }
736
+ const tokens = [];
737
+ for (const variable of extractCssVariableValues(content)) {
738
+ tokens.push({
739
+ path: variable.name,
740
+ value: variable.value,
741
+ type: classifyThemeToken(variable.name, variable.value),
742
+ line: variable.line,
743
+ });
744
+ }
745
+ if (/\.json$/.test(relPath)) {
746
+ try {
747
+ flattenJsonTokens(JSON.parse(content), [], tokens);
748
+ }
749
+ catch {
750
+ }
751
+ }
752
+ for (const match of content.matchAll(/["']?([A-Za-z_$][\w$-]*(?:Color|Radius|Spacing|Shadow|Font|Size|Height|Width|Padding|Gap|Background|Foreground|Border|Ring|Surface|Text)?|--[a-zA-Z0-9-_]+)["']?\s*:\s*(["'`])([^"'`]{1,220})\2/g)) {
753
+ tokens.push({
754
+ path: match[1],
755
+ value: match[3],
756
+ type: classifyThemeToken(match[1], match[3]),
757
+ line: (0, utils_1.lineNumberAt)(content, match.index ?? 0),
758
+ });
759
+ }
760
+ return (0, utils_1.uniqueBy)(tokens, (token) => `${token.path}:${token.value}`).slice(0, 300);
761
+ }
762
+ function extractInlineStyles(content) {
763
+ const styles = [];
764
+ for (const match of content.matchAll(/\bstyle\s*=\s*(["'])([^"']+)\1/g)) {
765
+ styles.push({
766
+ tag: readTagBefore(content, match.index ?? 0),
767
+ line: (0, utils_1.lineNumberAt)(content, match.index ?? 0),
768
+ raw: (0, utils_1.truncate)(match[0], 500),
769
+ properties: parseCssDeclarations(match[2]),
770
+ });
771
+ }
772
+ for (const match of content.matchAll(/\bstyle\s*=\s*\{\{([\s\S]{1,1200}?)\}\}/g)) {
773
+ styles.push({
774
+ tag: readTagBefore(content, match.index ?? 0),
775
+ line: (0, utils_1.lineNumberAt)(content, match.index ?? 0),
776
+ raw: (0, utils_1.truncate)(match[0], 700),
777
+ properties: parseJsStyleObject(match[1]),
778
+ });
779
+ }
780
+ return styles.slice(0, 120);
781
+ }
782
+ function nearestCssSelector(content, index) {
783
+ const openIndex = content.lastIndexOf('{', index);
784
+ if (openIndex < 0) {
785
+ return undefined;
786
+ }
787
+ const previousClose = content.lastIndexOf('}', openIndex);
788
+ const selector = content
789
+ .slice(previousClose + 1, openIndex)
790
+ .replace(/\/\*[\s\S]*?\*\//g, ' ')
791
+ .trim()
792
+ .replace(/\s+/g, ' ');
793
+ return selector ? (0, utils_1.truncate)(selector, 240) : undefined;
794
+ }
795
+ function parseCssDeclarations(block) {
796
+ return splitTopLevel(block, ';')
797
+ .map((declaration) => {
798
+ const separatorIndex = declaration.indexOf(':');
799
+ if (separatorIndex <= 0) {
800
+ return null;
801
+ }
802
+ const property = declaration.slice(0, separatorIndex).trim();
803
+ const value = declaration.slice(separatorIndex + 1).trim();
804
+ if (!property || !value || property.startsWith('@')) {
805
+ return null;
806
+ }
807
+ return { property, value };
808
+ })
809
+ .filter((item) => Boolean(item));
810
+ }
811
+ function parseJsStyleObject(block) {
812
+ return parseTopLevelObjectEntries(`{${block}}`)
813
+ .map((entry) => {
814
+ const value = firstStringLiteral(entry.rawValue) ?? cleanupBareValue(entry.rawValue);
815
+ if (!value) {
816
+ return null;
817
+ }
818
+ return {
819
+ property: (0, utils_1.toKebabCase)(entry.key),
820
+ value,
821
+ };
822
+ })
823
+ .filter((item) => Boolean(item));
824
+ }
825
+ function readTagBefore(content, index) {
826
+ const start = content.lastIndexOf('<', index);
827
+ if (start < 0 || ['/', '!', '?'].includes(content[start + 1] ?? '')) {
828
+ return undefined;
829
+ }
830
+ const match = content.slice(start + 1, index).match(/^([A-Za-z][A-Za-z0-9:._-]*)/);
831
+ return match?.[1];
832
+ }
833
+ function readBalancedCall(content, startIndex) {
834
+ const openIndex = content.indexOf('(', startIndex);
835
+ if (openIndex < 0) {
836
+ return undefined;
837
+ }
838
+ const args = readBalancedRange(content, openIndex, '(', ')');
839
+ return args ? content.slice(startIndex, openIndex) + args : undefined;
840
+ }
841
+ function readBalancedBlock(content, openIndex) {
842
+ return readBalancedRange(content, openIndex, '{', '}');
843
+ }
844
+ function readBalancedRange(content, openIndex, openChar, closeChar) {
845
+ if (content[openIndex] !== openChar) {
846
+ return undefined;
847
+ }
848
+ let depth = 0;
849
+ for (let index = openIndex; index < content.length; index += 1) {
850
+ const char = content[index];
851
+ if (char === '"' || char === "'" || char === '`') {
852
+ index = skipQuoted(content, index, char);
853
+ continue;
854
+ }
855
+ if (char === '/' && content[index + 1] === '/') {
856
+ const lineEnd = content.indexOf('\n', index + 2);
857
+ index = lineEnd >= 0 ? lineEnd : content.length;
858
+ continue;
859
+ }
860
+ if (char === '/' && content[index + 1] === '*') {
861
+ const commentEnd = content.indexOf('*/', index + 2);
862
+ index = commentEnd >= 0 ? commentEnd + 1 : content.length;
863
+ continue;
864
+ }
865
+ if (char === openChar) {
866
+ depth += 1;
867
+ continue;
868
+ }
869
+ if (char === closeChar) {
870
+ depth -= 1;
871
+ if (depth === 0) {
872
+ return content.slice(openIndex, index + 1);
873
+ }
874
+ }
875
+ }
876
+ return undefined;
877
+ }
878
+ function skipQuoted(content, quoteIndex, quote) {
879
+ for (let index = quoteIndex + 1; index < content.length; index += 1) {
880
+ if (content[index] === '\\') {
881
+ index += 1;
882
+ continue;
883
+ }
884
+ if (content[index] === quote) {
885
+ return index;
886
+ }
887
+ }
888
+ return content.length - 1;
889
+ }
890
+ function isInsideQuotedString(content, targetIndex) {
891
+ let quote = null;
892
+ for (let index = 0; index < targetIndex; index += 1) {
893
+ const char = content[index];
894
+ if (quote) {
895
+ if (char === '\\') {
896
+ index += 1;
897
+ continue;
898
+ }
899
+ if (char === quote) {
900
+ quote = null;
901
+ }
902
+ continue;
903
+ }
904
+ if (char === '/' && content[index + 1] === '/') {
905
+ const lineEnd = content.indexOf('\n', index + 2);
906
+ index = lineEnd >= 0 ? lineEnd : targetIndex;
907
+ continue;
908
+ }
909
+ if (char === '/' && content[index + 1] === '*') {
910
+ const commentEnd = content.indexOf('*/', index + 2);
911
+ index = commentEnd >= 0 ? commentEnd + 1 : targetIndex;
912
+ continue;
913
+ }
914
+ if (char === '"' || char === "'" || char === '`') {
915
+ quote = char;
916
+ }
917
+ }
918
+ return Boolean(quote);
919
+ }
920
+ function extractClassListsFromStrings(source) {
921
+ return analyzeClassSource(source).classes;
922
+ }
923
+ function analyzeClassSource(source) {
924
+ const classes = new Set();
925
+ const defaultClasses = new Set();
926
+ const conditionalClasses = new Set();
927
+ const directCandidate = directClassCandidate(source);
928
+ const directClasses = directCandidate ? splitClassList(directCandidate) : [];
929
+ if (directCandidate && looksLikeClassList(directClasses)) {
930
+ for (const className of directClasses) {
931
+ classes.add(className);
932
+ defaultClasses.add(className);
933
+ }
934
+ return toClassSourceAnalysis(classes, defaultClasses, conditionalClasses);
935
+ }
936
+ for (const literal of readStringLiteralRanges(source)) {
937
+ const literalClasses = splitClassList(literal.value);
938
+ if (!looksLikeClassList(literalClasses)) {
939
+ continue;
940
+ }
941
+ const targetSet = isConditionalClassLiteral(source, literal.start)
942
+ ? conditionalClasses
943
+ : defaultClasses;
944
+ for (const className of literalClasses) {
945
+ classes.add(className);
946
+ targetSet.add(className);
947
+ }
948
+ }
949
+ return toClassSourceAnalysis(classes, defaultClasses, conditionalClasses);
950
+ }
951
+ function toClassSourceAnalysis(classes, defaultClasses, conditionalClasses) {
952
+ const allClasses = Array.from(classes).sort();
953
+ const defaultClassList = Array.from(defaultClasses).sort();
954
+ const conditionalClassList = Array.from(conditionalClasses).sort();
955
+ return {
956
+ classes: allClasses,
957
+ defaultClasses: defaultClassList,
958
+ conditionalClasses: conditionalClassList,
959
+ decorativeAccentClasses: allClasses.filter(isDecorativeAccentClass).sort(),
960
+ };
961
+ }
962
+ function isConditionalClassLiteral(source, literalStart) {
963
+ const openIndex = source.indexOf('(');
964
+ const before = source.slice(Math.max(openIndex + 1, literalStart - 140), literalStart);
965
+ const sanitizedBefore = before.replace(/(["'`])(?:\\.|(?!\1)[\s\S])*\1/g, ' ');
966
+ const segmentStart = Math.max(sanitizedBefore.lastIndexOf(','), sanitizedBefore.lastIndexOf('('), sanitizedBefore.lastIndexOf('{'), sanitizedBefore.lastIndexOf('['));
967
+ const segment = sanitizedBefore.slice(segmentStart + 1);
968
+ return /(?:&&|\?|:)\s*[^"'`]*$/.test(segment);
969
+ }
970
+ function readStringLiteralRanges(source) {
971
+ const literals = [];
972
+ for (let index = 0; index < source.length; index += 1) {
973
+ const char = source[index];
974
+ if (char !== '"' && char !== "'" && char !== '`') {
975
+ continue;
976
+ }
977
+ const end = skipQuoted(source, index, char);
978
+ literals.push({
979
+ value: source.slice(index + 1, end),
980
+ start: index,
981
+ end,
982
+ });
983
+ index = end;
984
+ }
985
+ return literals;
986
+ }
987
+ function directClassCandidate(source) {
988
+ const trimmed = source.trim();
989
+ if (!trimmed ||
990
+ trimmed.startsWith('{') ||
991
+ /\b(?:cn|clsx|classNames|twMerge|cva|buttonVariants)\s*\(/.test(trimmed)) {
992
+ return undefined;
993
+ }
994
+ const withoutTemplateExpressions = trimmed.replace(/\$\{[\s\S]*?\}/g, ' ');
995
+ if (/[{}"'`,;]/.test(withoutTemplateExpressions)) {
996
+ return undefined;
997
+ }
998
+ return withoutTemplateExpressions;
999
+ }
1000
+ function readStringLiterals(source) {
1001
+ return readStringLiteralRanges(source).map((literal) => literal.value);
1002
+ }
1003
+ function looksLikeClassList(classes) {
1004
+ if (!classes.length) {
1005
+ return false;
1006
+ }
1007
+ const likelyCount = classes.filter(isLikelyStyleClass).length;
1008
+ return likelyCount >= Math.min(2, classes.length) || likelyCount / classes.length >= 0.5;
1009
+ }
1010
+ function isLikelyStyleClass(className) {
1011
+ const base = className.split(':').pop() ?? className;
1012
+ return (/^(?:-?m[trblxy]?|-?p[trblxy]?|flex|grid|block|inline|hidden|items-|justify-|content-|self-|gap-|space-|w-|h-|min-|max-|size-|rounded|border|bg-|from-|via-|to-|text-|font-|leading-|tracking-|shadow|ring|outline|opacity|transition|duration|ease|animate-|hover|focus|active|disabled|dark|sm|md|lg|xl|2xl|container|sr-only|absolute|relative|fixed|sticky|inset-|top-|right-|bottom-|left-|z-|overflow-|object-|aspect-|backdrop-|blur|scale-|translate-|rotate-)/.test(className) ||
1013
+ /^(?:button|btn|card|panel|surface|badge|pill|chip|nav|field|input|dialog|modal|drawer|sheet|container|wrapper|section|hero|header|footer|sidebar|menu)$/.test(base) ||
1014
+ className.includes('[') ||
1015
+ className.includes(']'));
1016
+ }
1017
+ function firstStringLiteral(source) {
1018
+ return readStringLiterals(source)[0];
1019
+ }
1020
+ function extractNamedObjectBlock(source, name) {
1021
+ const pattern = new RegExp(`\\b${(0, utils_1.escapeRegExp)(name)}\\s*:`, 'g');
1022
+ for (const match of source.matchAll(pattern)) {
1023
+ const openIndex = source.indexOf('{', (match.index ?? 0) + match[0].length);
1024
+ if (openIndex < 0) {
1025
+ continue;
1026
+ }
1027
+ return readBalancedBlock(source, openIndex);
1028
+ }
1029
+ return undefined;
1030
+ }
1031
+ function parseVariantGroups(block) {
1032
+ const variants = {};
1033
+ for (const entry of parseTopLevelObjectEntries(block)) {
1034
+ if (!entry.rawValue.trim().startsWith('{')) {
1035
+ continue;
1036
+ }
1037
+ const values = parseClassValueMap(entry.rawValue);
1038
+ if (Object.keys(values).length) {
1039
+ variants[entry.key] = values;
1040
+ }
1041
+ }
1042
+ return variants;
1043
+ }
1044
+ function parseStringValueMap(block) {
1045
+ const values = {};
1046
+ for (const entry of parseTopLevelObjectEntries(block)) {
1047
+ const value = firstStringLiteral(entry.rawValue) ?? cleanupBareValue(entry.rawValue);
1048
+ if (value) {
1049
+ values[entry.key] = value;
1050
+ }
1051
+ }
1052
+ return values;
1053
+ }
1054
+ function parseClassValueMap(block) {
1055
+ const values = {};
1056
+ for (const entry of parseTopLevelObjectEntries(block)) {
1057
+ const classes = extractClassListsFromStrings(entry.rawValue);
1058
+ if (classes.length) {
1059
+ values[entry.key] = classes;
1060
+ }
1061
+ }
1062
+ return values;
1063
+ }
1064
+ function parseTopLevelObjectEntries(block) {
1065
+ const source = stripOuterBraces(block);
1066
+ const entries = [];
1067
+ let index = 0;
1068
+ while (index < source.length) {
1069
+ index = skipSeparators(source, index);
1070
+ if (index >= source.length) {
1071
+ break;
1072
+ }
1073
+ const keyResult = readObjectKey(source, index);
1074
+ if (!keyResult) {
1075
+ index += 1;
1076
+ continue;
1077
+ }
1078
+ index = skipWhitespace(source, keyResult.nextIndex);
1079
+ if (source[index] !== ':') {
1080
+ index += 1;
1081
+ continue;
1082
+ }
1083
+ index = skipWhitespace(source, index + 1);
1084
+ const valueEnd = readObjectValueEnd(source, index);
1085
+ const rawValue = source.slice(index, valueEnd).trim();
1086
+ if (rawValue) {
1087
+ entries.push({ key: keyResult.key, rawValue });
1088
+ }
1089
+ index = valueEnd + 1;
1090
+ }
1091
+ return entries;
1092
+ }
1093
+ function stripOuterBraces(block) {
1094
+ const trimmed = block.trim();
1095
+ return trimmed.startsWith('{') && trimmed.endsWith('}') ? trimmed.slice(1, -1) : trimmed;
1096
+ }
1097
+ function skipSeparators(source, index) {
1098
+ let cursor = index;
1099
+ while (cursor < source.length && /[\s,]/.test(source[cursor])) {
1100
+ cursor += 1;
1101
+ }
1102
+ return cursor;
1103
+ }
1104
+ function skipWhitespace(source, index) {
1105
+ let cursor = index;
1106
+ while (cursor < source.length && /\s/.test(source[cursor])) {
1107
+ cursor += 1;
1108
+ }
1109
+ return cursor;
1110
+ }
1111
+ function readObjectKey(source, index) {
1112
+ const char = source[index];
1113
+ if (char === '"' || char === "'" || char === '`') {
1114
+ const end = skipQuoted(source, index, char);
1115
+ return { key: source.slice(index + 1, end), nextIndex: end + 1 };
1116
+ }
1117
+ const match = source.slice(index).match(/^([A-Za-z_$][\w$-]*|true|false|\d+)/);
1118
+ return match ? { key: match[1], nextIndex: index + match[0].length } : undefined;
1119
+ }
1120
+ function readObjectValueEnd(source, index) {
1121
+ let cursor = index;
1122
+ let parenDepth = 0;
1123
+ let blockDepth = 0;
1124
+ let arrayDepth = 0;
1125
+ while (cursor < source.length) {
1126
+ const char = source[cursor];
1127
+ if (char === '"' || char === "'" || char === '`') {
1128
+ cursor = skipQuoted(source, cursor, char) + 1;
1129
+ continue;
1130
+ }
1131
+ if (char === '(') {
1132
+ parenDepth += 1;
1133
+ }
1134
+ else if (char === ')') {
1135
+ parenDepth = Math.max(0, parenDepth - 1);
1136
+ }
1137
+ else if (char === '{') {
1138
+ blockDepth += 1;
1139
+ }
1140
+ else if (char === '}') {
1141
+ if (blockDepth === 0) {
1142
+ break;
1143
+ }
1144
+ blockDepth -= 1;
1145
+ }
1146
+ else if (char === '[') {
1147
+ arrayDepth += 1;
1148
+ }
1149
+ else if (char === ']') {
1150
+ arrayDepth = Math.max(0, arrayDepth - 1);
1151
+ }
1152
+ else if (char === ',' && parenDepth === 0 && blockDepth === 0 && arrayDepth === 0) {
1153
+ break;
1154
+ }
1155
+ cursor += 1;
1156
+ }
1157
+ return cursor;
1158
+ }
1159
+ function splitTopLevel(source, delimiter) {
1160
+ const parts = [];
1161
+ let start = 0;
1162
+ let parenDepth = 0;
1163
+ let blockDepth = 0;
1164
+ let arrayDepth = 0;
1165
+ for (let index = 0; index < source.length; index += 1) {
1166
+ const char = source[index];
1167
+ if (char === '"' || char === "'" || char === '`') {
1168
+ index = skipQuoted(source, index, char);
1169
+ continue;
1170
+ }
1171
+ if (char === '(') {
1172
+ parenDepth += 1;
1173
+ }
1174
+ else if (char === ')') {
1175
+ parenDepth = Math.max(0, parenDepth - 1);
1176
+ }
1177
+ else if (char === '{') {
1178
+ blockDepth += 1;
1179
+ }
1180
+ else if (char === '}') {
1181
+ blockDepth = Math.max(0, blockDepth - 1);
1182
+ }
1183
+ else if (char === '[') {
1184
+ arrayDepth += 1;
1185
+ }
1186
+ else if (char === ']') {
1187
+ arrayDepth = Math.max(0, arrayDepth - 1);
1188
+ }
1189
+ else if (char === delimiter && parenDepth === 0 && blockDepth === 0 && arrayDepth === 0) {
1190
+ parts.push(source.slice(start, index));
1191
+ start = index + 1;
1192
+ }
1193
+ }
1194
+ parts.push(source.slice(start));
1195
+ return parts;
1196
+ }
1197
+ function cleanupBareValue(value) {
1198
+ const cleaned = value
1199
+ .trim()
1200
+ .replace(/,$/, '')
1201
+ .replace(/^['"`]|['"`]$/g, '');
1202
+ return cleaned && cleaned.length < 220 ? cleaned : undefined;
1203
+ }
1204
+ function classifyThemeToken(key, value) {
1205
+ const haystack = `${key} ${value}`.toLowerCase();
1206
+ if (/(#[0-9a-f]{3,8}\b|rgb\(|rgba\(|hsl\(|hsla\(|oklch\(|color|foreground|background|surface|border|ring|accent|primary|secondary|muted|destructive)/.test(haystack)) {
1207
+ return 'color';
1208
+ }
1209
+ if (/(radius|rounded|border-radius)/.test(haystack)) {
1210
+ return 'radius';
1211
+ }
1212
+ if (/(spacing|space|gap|padding|margin|inset)/.test(haystack)) {
1213
+ return 'spacing';
1214
+ }
1215
+ if (/(font|family|weight|leading|tracking|letter|line-height)/.test(haystack)) {
1216
+ return 'font';
1217
+ }
1218
+ if (/(shadow|elevation|drop-shadow)/.test(haystack)) {
1219
+ return 'shadow';
1220
+ }
1221
+ if (/(size|width|height|text-|rem|px|vh|vw|%)/.test(haystack)) {
1222
+ return 'size';
1223
+ }
1224
+ return 'unknown';
1225
+ }
1226
+ function flattenJsonTokens(value, pathParts, tokens) {
1227
+ if (value === null || value === undefined) {
1228
+ return;
1229
+ }
1230
+ if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
1231
+ const pathValue = pathParts.join('.');
1232
+ const stringValue = String(value);
1233
+ if (pathValue) {
1234
+ tokens.push({
1235
+ path: pathValue,
1236
+ value: stringValue,
1237
+ type: classifyThemeToken(pathValue, stringValue),
1238
+ });
1239
+ }
1240
+ return;
1241
+ }
1242
+ if (Array.isArray(value)) {
1243
+ value
1244
+ .slice(0, 80)
1245
+ .forEach((item, index) => flattenJsonTokens(item, [...pathParts, String(index)], tokens));
1246
+ return;
1247
+ }
1248
+ if (typeof value === 'object') {
1249
+ for (const [key, childValue] of Object.entries(value).slice(0, 200)) {
1250
+ flattenJsonTokens(childValue, [...pathParts, key], tokens);
1251
+ }
1252
+ }
1253
+ }
1254
+ function readOpeningTags(content) {
1255
+ const tags = [];
1256
+ for (let index = 0; index < content.length; index += 1) {
1257
+ if (content[index] !== '<' || !/[A-Za-z]/.test(content[index + 1] ?? '')) {
1258
+ continue;
1259
+ }
1260
+ const start = index;
1261
+ let cursor = index + 1;
1262
+ while (cursor < content.length && /[A-Za-z0-9:._-]/.test(content[cursor])) {
1263
+ cursor += 1;
1264
+ }
1265
+ const originalTag = content.slice(index + 1, cursor);
1266
+ if (!originalTag) {
1267
+ continue;
1268
+ }
1269
+ let braceDepth = 0;
1270
+ let end = -1;
1271
+ for (; cursor < content.length; cursor += 1) {
1272
+ const char = content[cursor];
1273
+ if (char === '"' || char === "'" || char === '`') {
1274
+ cursor = skipQuoted(content, cursor, char);
1275
+ continue;
1276
+ }
1277
+ if (char === '{') {
1278
+ braceDepth += 1;
1279
+ continue;
1280
+ }
1281
+ if (char === '}') {
1282
+ braceDepth = Math.max(0, braceDepth - 1);
1283
+ continue;
1284
+ }
1285
+ if (char === '>' && braceDepth === 0) {
1286
+ end = cursor;
1287
+ break;
1288
+ }
1289
+ }
1290
+ if (end < 0) {
1291
+ continue;
1292
+ }
1293
+ const attrs = content.slice(start + originalTag.length + 1, end);
1294
+ tags.push({
1295
+ originalTag,
1296
+ attrs,
1297
+ start,
1298
+ end,
1299
+ selfClosing: /\/\s*$/.test(attrs),
1300
+ });
1301
+ index = end;
1302
+ }
1303
+ annotateOpeningTags(content, tags);
1304
+ return tags;
1305
+ }
1306
+ function annotateOpeningTags(content, tags) {
1307
+ for (const tag of tags) {
1308
+ tag.closeEnd = findClosingTagEnd(content, tag);
1309
+ }
1310
+ for (const [index, tag] of tags.entries()) {
1311
+ let parentIndex;
1312
+ let parentStart = -1;
1313
+ for (let candidateIndex = 0; candidateIndex < index; candidateIndex += 1) {
1314
+ const candidate = tags[candidateIndex];
1315
+ const candidateCloseEnd = candidate.closeEnd ?? candidate.end;
1316
+ if (candidate.start < tag.start &&
1317
+ candidateCloseEnd > tag.end &&
1318
+ candidate.start > parentStart) {
1319
+ parentIndex = candidateIndex;
1320
+ parentStart = candidate.start;
1321
+ }
1322
+ }
1323
+ tag.parentIndex = parentIndex;
1324
+ tag.depth = parentIndex === undefined ? 0 : Math.min(24, (tags[parentIndex].depth ?? 0) + 1);
1325
+ }
1326
+ }
1327
+ function findClosingTagEnd(content, tagInfo) {
1328
+ if (tagInfo.selfClosing || isVoidHtmlElement(tagInfo.originalTag)) {
1329
+ return tagInfo.end;
1330
+ }
1331
+ const tagPattern = new RegExp(`<\\/?${(0, utils_1.escapeRegExp)(tagInfo.originalTag)}(?=\\s|>|\\/)[\\s\\S]*?>`, 'g');
1332
+ tagPattern.lastIndex = tagInfo.end + 1;
1333
+ let depth = 1;
1334
+ for (const match of content.matchAll(tagPattern)) {
1335
+ const raw = match[0];
1336
+ if (raw.startsWith('</')) {
1337
+ depth -= 1;
1338
+ if (depth === 0) {
1339
+ return (match.index ?? 0) + raw.length;
1340
+ }
1341
+ continue;
1342
+ }
1343
+ if (!/\/\s*>$/.test(raw)) {
1344
+ depth += 1;
1345
+ }
1346
+ }
1347
+ return undefined;
1348
+ }
1349
+ function isVoidHtmlElement(tag) {
1350
+ return /^(area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)$/i.test(tag);
1351
+ }
1352
+ function extractElementLabel(content, tagInfo) {
1353
+ if (tagInfo.selfClosing || tagInfo.originalTag.includes('.')) {
1354
+ return undefined;
1355
+ }
1356
+ const closeTag = `</${tagInfo.originalTag}>`;
1357
+ const closeIndex = content.indexOf(closeTag, tagInfo.end + 1);
1358
+ if (closeIndex < 0 || closeIndex - tagInfo.end > 1800) {
1359
+ return undefined;
1360
+ }
1361
+ const label = stripTags(content.slice(tagInfo.end + 1, closeIndex))
1362
+ .replace(/\{[\s\S]*?\}/g, ' ')
1363
+ .trim()
1364
+ .replace(/\s+/g, ' ');
1365
+ return label ? (0, utils_1.truncate)(label, 120) : undefined;
1366
+ }
1367
+ function readClassSource(attrs) {
1368
+ const classNames = ['className', 'class', 'class:list', 'classList', ':class', 'ngClass'];
1369
+ const sources = [];
1370
+ for (const className of classNames) {
1371
+ const source = readAttrSource(attrs, className);
1372
+ if (source) {
1373
+ sources.push(source);
1374
+ }
1375
+ }
1376
+ return sources.length ? sources.join(' ') : undefined;
1377
+ }
1378
+ function readUiProps(attrs) {
1379
+ const props = {};
1380
+ const names = [
1381
+ 'variant',
1382
+ 'size',
1383
+ 'intent',
1384
+ 'tone',
1385
+ 'color',
1386
+ 'appearance',
1387
+ 'type',
1388
+ 'role',
1389
+ 'href',
1390
+ 'disabled',
1391
+ 'checked',
1392
+ 'aria-label',
1393
+ 'data-slot',
1394
+ 'asChild',
1395
+ ];
1396
+ for (const name of names) {
1397
+ const value = (0, utils_1.readAttr)(attrs, name) ?? readAttrSource(attrs, name);
1398
+ if (value) {
1399
+ props[name] = (0, utils_1.truncate)(cleanupAttrValue(value), 160);
1400
+ continue;
1401
+ }
1402
+ if (hasBooleanAttr(attrs, name)) {
1403
+ props[name] = 'true';
1404
+ }
1405
+ }
1406
+ return props;
1407
+ }
1408
+ function readVariantProps(props) {
1409
+ const variantProps = {};
1410
+ for (const name of ['variant', 'size', 'intent', 'tone', 'color', 'appearance', 'type']) {
1411
+ const value = props[name];
1412
+ if (value) {
1413
+ variantProps[name] = value;
1414
+ }
1415
+ }
1416
+ return variantProps;
1417
+ }
1418
+ function readAttrSource(attrs, name) {
1419
+ const pattern = new RegExp(`(?:^|\\s)${(0, utils_1.escapeRegExp)(name)}\\s*=`, 'g');
1420
+ const match = pattern.exec(attrs);
1421
+ if (!match) {
1422
+ return undefined;
1423
+ }
1424
+ return readAttrValueSource(attrs, match.index + match[0].length);
1425
+ }
1426
+ function readAttrValueSource(attrs, index) {
1427
+ const cursor = skipWhitespace(attrs, index);
1428
+ const char = attrs[cursor];
1429
+ if (!char) {
1430
+ return undefined;
1431
+ }
1432
+ if (char === '"' || char === "'" || char === '`') {
1433
+ const end = skipQuoted(attrs, cursor, char);
1434
+ return attrs.slice(cursor + 1, end);
1435
+ }
1436
+ if (char === '{') {
1437
+ return readBalancedBlock(attrs, cursor);
1438
+ }
1439
+ const match = attrs.slice(cursor).match(/^[^\s>]+/);
1440
+ return match?.[0];
1441
+ }
1442
+ function cleanupAttrValue(value) {
1443
+ let cleaned = value.trim();
1444
+ if (cleaned.startsWith('{') && cleaned.endsWith('}')) {
1445
+ cleaned = cleaned.slice(1, -1).trim();
1446
+ }
1447
+ const first = cleaned[0];
1448
+ const last = cleaned[cleaned.length - 1];
1449
+ if ((first === '"' || first === "'" || first === '`') && first === last) {
1450
+ cleaned = cleaned.slice(1, -1).trim();
1451
+ }
1452
+ return cleaned;
1453
+ }
1454
+ function hasBooleanAttr(attrs, name) {
1455
+ return new RegExp(`(?:^|\\s)${(0, utils_1.escapeRegExp)(name)}(?:\\s|$|>)`).test(attrs);
1456
+ }
1457
+ function groupTailwindClasses(classes) {
1458
+ const groups = {};
1459
+ for (const className of classes) {
1460
+ const variants = className.split(':');
1461
+ const base = variants[variants.length - 1];
1462
+ if (variants.length > 1) {
1463
+ const prefixes = variants.slice(0, -1);
1464
+ if (prefixes.some((prefix) => /^(sm|md|lg|xl|2xl|max-|min-)/.test(prefix))) {
1465
+ addClassGroup(groups, 'responsive', className);
1466
+ }
1467
+ if (prefixes.some((prefix) => /^(hover|focus|active|disabled|visited|dark|group|peer|aria|data)/.test(prefix))) {
1468
+ addClassGroup(groups, 'state', className);
1469
+ }
1470
+ }
1471
+ if (/^(flex|grid|block|inline|hidden|items-|justify-|content-|self-|place-|order-|col-|row-)/.test(base)) {
1472
+ addClassGroup(groups, 'layout', className);
1473
+ }
1474
+ else if (/^(?:-?m[trblxy]?|-?p[trblxy]?|gap-|space-|inset-|top-|right-|bottom-|left-)/.test(base)) {
1475
+ addClassGroup(groups, 'spacing', className);
1476
+ }
1477
+ else if (/^(w-|h-|min-|max-|size-|aspect-|basis-|grow|shrink)/.test(base)) {
1478
+ addClassGroup(groups, 'sizing', className);
1479
+ }
1480
+ else if (/^(bg-|from-|via-|to-|text-|decoration-|accent-|caret-|fill-|stroke-)/.test(base)) {
1481
+ addClassGroup(groups, 'color', className);
1482
+ }
1483
+ else if (/^(rounded|border|divide-|outline|ring)/.test(base)) {
1484
+ addClassGroup(groups, 'border', className);
1485
+ }
1486
+ else if (/^(font-|text-|leading-|tracking-|line-clamp|list-|whitespace-|break-|truncate)/.test(base)) {
1487
+ addClassGroup(groups, 'typography', className);
1488
+ }
1489
+ else if (/^(shadow|opacity|blur|backdrop|mix-|bg-blend|drop-shadow|filter)/.test(base)) {
1490
+ addClassGroup(groups, 'effects', className);
1491
+ }
1492
+ else if (/^(absolute|relative|fixed|sticky|z-|overflow-|object-)/.test(base)) {
1493
+ addClassGroup(groups, 'position', className);
1494
+ }
1495
+ else if (/^(transition|duration|ease|delay|animate-|transform|scale-|rotate-|translate-)/.test(base)) {
1496
+ addClassGroup(groups, 'motion', className);
1497
+ }
1498
+ else {
1499
+ addClassGroup(groups, 'other', className);
1500
+ }
1501
+ }
1502
+ return Object.fromEntries(Object.entries(groups).map(([group, values]) => [group, (0, utils_1.unique)(values).slice(0, 80)]));
1503
+ }
1504
+ function addClassGroup(groups, group, className) {
1505
+ groups[group] = [...(groups[group] ?? []), className];
1506
+ }
1507
+ function mergeClassGroups(groups) {
1508
+ const merged = {};
1509
+ for (const group of groups) {
1510
+ for (const [name, values] of Object.entries(group)) {
1511
+ merged[name] = (0, utils_1.unique)([...(merged[name] ?? []), ...values]).slice(0, 120);
1512
+ }
1513
+ }
1514
+ return merged;
1515
+ }
1516
+ function mergeVariantProps(variants) {
1517
+ const merged = {};
1518
+ for (const variant of variants) {
1519
+ for (const [name, value] of Object.entries(variant)) {
1520
+ merged[name] = (0, utils_1.unique)([...(merged[name] ?? []), value]).slice(0, 40);
1521
+ }
1522
+ }
1523
+ return merged;
1524
+ }
1525
+ function isCompositionElement(element) {
1526
+ return ([
1527
+ 'section',
1528
+ 'layout',
1529
+ 'card',
1530
+ 'form',
1531
+ 'nav',
1532
+ 'table',
1533
+ 'heading',
1534
+ 'badge',
1535
+ 'button',
1536
+ 'input',
1537
+ ].includes(element.kind) ||
1538
+ Boolean(element.layoutRole) ||
1539
+ element.classes.some((className) => /^(?:section-frame|context-surface|brand-spectrum-card|load-in-section|reveal-up)$/.test(className.replace(/^[a-z0-9-]+:/, ''))));
1540
+ }
1541
+ function filterClasses(classes, predicate) {
1542
+ return (0, utils_1.unique)(classes.filter(predicate));
1543
+ }
1544
+ function isResponsiveClass(className) {
1545
+ return /^(?:sm|md|lg|xl|2xl|max-|min-):/.test(className);
1546
+ }
1547
+ function isSpacingClass(className) {
1548
+ const base = (0, utils_1.classBase)(className);
1549
+ return /^(?:-?m[trblxy]?|-?p[trblxy]?|gap-|space-|inset-|top-|right-|bottom-|left-)/.test(base);
1550
+ }
1551
+ function isSurfaceClass(className) {
1552
+ const base = (0, utils_1.classBase)(className);
1553
+ return (/^(?:bg-|border|rounded|shadow|ring|outline|divide-|opacity|backdrop|blur|drop-shadow)/.test(base) ||
1554
+ ['section-frame', 'context-surface', 'brand-spectrum-card', 'brand-spectrum-line'].includes(base));
1555
+ }
1556
+ function isDecorativeAccentClass(className) {
1557
+ const base = (0, utils_1.classBase)(className);
1558
+ return (['brand-spectrum-card', 'brand-spectrum-line'].includes(base) ||
1559
+ base === 'bg-[var(--brand-spectrum-gradient)]' ||
1560
+ /^(?:shadow|drop-shadow)-\[.*rgba\(96,48,232/.test(base) ||
1561
+ /^(?:bg|border)-\[rgba\(96,48,232/.test(base));
1562
+ }
1563
+ function isTypographyClass(className) {
1564
+ const base = (0, utils_1.classBase)(className);
1565
+ return /^(?:text-|font-|leading-|tracking-|line-clamp|whitespace-|break-|truncate|max-w-|text-balance)/.test(base);
1566
+ }
1567
+ function groupClassExpressionsByTarget(expressions) {
1568
+ const grouped = {};
1569
+ for (const expression of expressions) {
1570
+ const target = expression.target ?? expression.kind;
1571
+ grouped[target] = [
1572
+ ...(grouped[target] ?? []),
1573
+ {
1574
+ kind: expression.kind,
1575
+ line: expression.line,
1576
+ classes: expression.classes,
1577
+ defaultClasses: expression.defaultClasses,
1578
+ conditionalClasses: expression.conditionalClasses,
1579
+ decorativeAccentClasses: expression.classes.filter(isDecorativeAccentClass),
1580
+ classGroups: groupTailwindClasses(expression.classes),
1581
+ raw: (0, utils_1.truncate)(expression.raw, 600),
1582
+ },
1583
+ ].slice(0, 40);
1584
+ }
1585
+ return grouped;
1586
+ }
1587
+ function buildComponentSlots(elements) {
1588
+ return elements
1589
+ .filter((element) => /^(Card|CardHeader|CardContent|CardFooter|CardTitle|CardDescription|Container|Section)$/i.test(element.originalTag))
1590
+ .slice(0, 80)
1591
+ .map((element) => ({
1592
+ index: element.index,
1593
+ tag: element.originalTag,
1594
+ kind: element.kind,
1595
+ layoutRole: element.layoutRole,
1596
+ line: element.line,
1597
+ classes: element.classes,
1598
+ defaultClasses: element.defaultClasses,
1599
+ conditionalClasses: element.conditionalClasses,
1600
+ decorativeAccentClasses: element.decorativeAccentClasses,
1601
+ classGroups: element.classGroups,
1602
+ parentIndex: element.parentIndex,
1603
+ parentTag: element.parentTag,
1604
+ childSummary: element.childSummary,
1605
+ label: element.label,
1606
+ }));
1607
+ }
1608
+ function createUiCompositionGuidance(file) {
1609
+ const classes = new Set(file.classReferences);
1610
+ const guidance = [];
1611
+ if (classes.has('sm:section-frame') && classes.has('bg-transparent')) {
1612
+ guidance.push('Preserve mobile-first shell behavior: transparent/unframed on mobile, section-frame at sm and above.');
1613
+ }
1614
+ if (classes.has('p-[var(--panel-padding)]') &&
1615
+ (classes.has('p-3') || classes.has('p-4') || classes.has('sm:p-6') || classes.has('sm:p-8'))) {
1616
+ guidance.push('Do not apply panel padding everywhere; card and section padding is slot-specific and responsive.');
1617
+ }
1618
+ if (classes.has('hidden') &&
1619
+ (classes.has('sm:block') || classes.has('md:block') || classes.has('sm:inline-flex'))) {
1620
+ guidance.push('Preserve responsive visibility rules; some secondary copy, cards, and CTAs are intentionally hidden on small screens.');
1621
+ }
1622
+ if (classes.has('max-w-[11ch]') ||
1623
+ classes.has('leading-[0.98]') ||
1624
+ classes.has('sm:text-balance')) {
1625
+ guidance.push('Hero headline typography is exact: keep narrow mobile measure, tight leading, and desktop balancing classes.');
1626
+ }
1627
+ if (file.imports.some((item) => item.includes('/ui/card'))) {
1628
+ guidance.push('Card is a shell primitive; use CardHeader/CardContent/CardFooter usage classes for padding and hierarchy.');
1629
+ }
1630
+ if (classes.has('section-frame') || classes.has('context-surface')) {
1631
+ guidance.push('Use section-frame/context-surface only for framed technical panels, not every repeated card.');
1632
+ }
1633
+ if (file.classReferences.some(isDecorativeAccentClass)) {
1634
+ guidance.push('Treat brand-spectrum-card and purple glow shadow classes as accent/highlight-only; do not use them for default repeated cards unless the source element is explicitly current, highlighted, or featured.');
1635
+ }
1636
+ return guidance;
1637
+ }
1638
+ function createUiPatternGuidance(kind, decorativeAccentClasses) {
1639
+ const guidance = [];
1640
+ if (kind === 'card') {
1641
+ guidance.push('Use defaultClasses for ordinary cards; decorativeAccentClasses are opt-in highlight treatments.');
1642
+ }
1643
+ if (decorativeAccentClasses.length) {
1644
+ guidance.push('Do not apply decorativeAccentClasses by default. They are reserved for explicitly featured/current/highlighted states.');
1645
+ }
1646
+ return guidance;
1647
+ }
1648
+ function extractTailwindUtilities(classReferences) {
1649
+ return classReferences
1650
+ .filter((className) => /^(?:-?m[trblxy]?|-?p[trblxy]?|flex|grid|block|inline|hidden|items-|justify-|gap-|space-|w-|h-|min-|max-|rounded|border|bg-|text-|font-|leading-|tracking-|shadow|ring|opacity|transition|duration|ease|hover:|focus:|active:|disabled:|dark:|sm:|md:|lg:|xl:|2xl:|container|sr-only)/.test(className))
1651
+ .slice(0, 160);
1652
+ }
1653
+ function extractPackageDependencies(relPath, content) {
1654
+ const baseName = path.basename(relPath);
1655
+ if (baseName === 'package.json' || baseName === 'composer.json') {
1656
+ try {
1657
+ const parsed = JSON.parse(content);
1658
+ return (0, utils_1.unique)([
1659
+ ...Object.keys(parsed.dependencies ?? {}),
1660
+ ...Object.keys(parsed.devDependencies ?? {}),
1661
+ ...Object.keys(parsed.require ?? {}),
1662
+ ...Object.keys(parsed.requireDev ?? {}),
1663
+ ...Object.keys(parsed['require-dev'] ?? {}),
1664
+ ]).sort();
1665
+ }
1666
+ catch {
1667
+ return [];
1668
+ }
1669
+ }
1670
+ if (baseName === 'requirements.txt') {
1671
+ return (0, utils_1.unique)(content
1672
+ .split('\n')
1673
+ .map((line) => line
1674
+ .trim()
1675
+ .replace(/\s*#.*$/, '')
1676
+ .split(/[<>=~!]/)[0])
1677
+ .filter((line) => Boolean(line) && !line.startsWith('-'))).sort();
1678
+ }
1679
+ if (baseName === 'Gemfile') {
1680
+ return (0, utils_1.unique)(Array.from(content.matchAll(/^\s*gem\s+['"]([^'"]+)['"]/gm)).map((match) => match[1])).sort();
1681
+ }
1682
+ if (baseName === 'go.mod') {
1683
+ return (0, utils_1.unique)(Array.from(content.matchAll(/^\s*(?:require\s+)?([a-zA-Z0-9_.\-/]+)\s+v\d/gm)).map((match) => match[1])).sort();
1684
+ }
1685
+ if (baseName === 'Cargo.toml' || baseName === 'pyproject.toml') {
1686
+ return (0, utils_1.unique)(Array.from(content.matchAll(/^\s*([A-Za-z0-9_.-]+)\s*=\s*["{]/gm)).map((match) => match[1])).sort();
1687
+ }
1688
+ return [];
1689
+ }
1690
+ function extractUiElements(content, relPath) {
1691
+ if (!isUiFile(relPath) &&
1692
+ !isTemplateFile(relPath) &&
1693
+ !/\.(html|htm|php|phtml|erb|twig|blade\.php)$/.test(relPath)) {
1694
+ return [];
1695
+ }
1696
+ const elements = [];
1697
+ const tags = readOpeningTags(content);
1698
+ for (const [index, tagInfo] of tags.entries()) {
1699
+ const tag = tagInfo.originalTag.toLowerCase();
1700
+ const attrs = tagInfo.attrs;
1701
+ const label = extractElementLabel(content, tagInfo);
1702
+ const classSource = readClassSource(attrs);
1703
+ const classAnalysis = classSource ? analyzeClassSource(classSource) : undefined;
1704
+ const classes = classAnalysis?.classes ?? [];
1705
+ const role = (0, utils_1.readAttr)(attrs, 'role');
1706
+ const props = readUiProps(attrs);
1707
+ const variants = readVariantProps(props);
1708
+ const kind = classifyUiElement(tagInfo.originalTag, attrs, classes, role);
1709
+ const parentTagInfo = tagInfo.parentIndex === undefined ? undefined : tags[tagInfo.parentIndex];
1710
+ const parentClassSource = parentTagInfo ? readClassSource(parentTagInfo.attrs) : undefined;
1711
+ const parentClasses = parentClassSource ? analyzeClassSource(parentClassSource).classes : [];
1712
+ const parentRole = parentTagInfo ? (0, utils_1.readAttr)(parentTagInfo.attrs, 'role') : undefined;
1713
+ const parentKind = parentTagInfo
1714
+ ? classifyUiElement(parentTagInfo.originalTag, parentTagInfo.attrs, parentClasses, parentRole)
1715
+ : undefined;
1716
+ const layoutRole = deriveLayoutRole(tagInfo.originalTag, kind, classes, label);
1717
+ const responsiveClasses = classes.filter(isResponsiveClass);
1718
+ const stateClasses = classes.filter(isStateClass);
1719
+ const childSummary = summarizeDirectUiChildren(content, tags, index);
1720
+ if (kind === 'unknown') {
1721
+ continue;
1722
+ }
1723
+ elements.push({
1724
+ index,
1725
+ kind,
1726
+ tag,
1727
+ originalTag: tagInfo.originalTag,
1728
+ line: (0, utils_1.lineNumberAt)(content, tagInfo.start),
1729
+ ...(tagInfo.depth ? { depth: tagInfo.depth } : {}),
1730
+ ...(tagInfo.parentIndex !== undefined ? { parentIndex: tagInfo.parentIndex } : {}),
1731
+ ...(parentKind && parentKind !== 'unknown' ? { parentKind } : {}),
1732
+ ...(parentTagInfo ? { parentTag: parentTagInfo.originalTag } : {}),
1733
+ ...(parentClasses.length ? { parentClasses: parentClasses.slice(0, 40) } : {}),
1734
+ ...(parentClasses.length ? { parentClassGroups: groupTailwindClasses(parentClasses) } : {}),
1735
+ ...(layoutRole ? { layoutRole } : {}),
1736
+ ...(label ? { label } : {}),
1737
+ classes,
1738
+ ...(classAnalysis?.defaultClasses.length
1739
+ ? { defaultClasses: classAnalysis.defaultClasses.slice(0, 80) }
1740
+ : {}),
1741
+ ...(classAnalysis?.conditionalClasses.length
1742
+ ? { conditionalClasses: classAnalysis.conditionalClasses.slice(0, 80) }
1743
+ : {}),
1744
+ ...(classAnalysis?.decorativeAccentClasses.length
1745
+ ? { decorativeAccentClasses: classAnalysis.decorativeAccentClasses.slice(0, 40) }
1746
+ : {}),
1747
+ ...(classSource ? { classSource: (0, utils_1.truncate)(classSource, 700) } : {}),
1748
+ classGroups: groupTailwindClasses(classes),
1749
+ ...(responsiveClasses.length ? { responsiveClasses } : {}),
1750
+ ...(stateClasses.length ? { stateClasses } : {}),
1751
+ ...(childSummary.length ? { childSummary } : {}),
1752
+ ...((0, utils_1.readAttr)(attrs, 'id') ? { id: (0, utils_1.readAttr)(attrs, 'id') } : {}),
1753
+ ...(role ? { role } : {}),
1754
+ ...((0, utils_1.readAttr)(attrs, 'type') ? { type: (0, utils_1.readAttr)(attrs, 'type') } : {}),
1755
+ ...((0, utils_1.readAttr)(attrs, 'href') ? { href: (0, utils_1.readAttr)(attrs, 'href') } : {}),
1756
+ ...((0, utils_1.readAttr)(attrs, 'action') ? { action: (0, utils_1.readAttr)(attrs, 'action') } : {}),
1757
+ ...((0, utils_1.readAttr)(attrs, 'method') ? { method: (0, utils_1.readAttr)(attrs, 'method')?.toUpperCase() } : {}),
1758
+ ...(Object.keys(props).length ? { props } : {}),
1759
+ ...(Object.keys(variants).length ? { variants } : {}),
1760
+ evidence: `<${tagInfo.originalTag}${classes.length ? ` class="${classes.slice(0, 6).join(' ')}"` : ''}>`,
1761
+ });
1762
+ }
1763
+ return elements.slice(0, 240);
1764
+ }
1765
+ function summarizeDirectUiChildren(content, tags, parentIndex) {
1766
+ return tags
1767
+ .map((tagInfo, index) => ({ tagInfo, index }))
1768
+ .filter(({ tagInfo }) => tagInfo.parentIndex === parentIndex)
1769
+ .map(({ tagInfo, index }) => {
1770
+ const classSource = readClassSource(tagInfo.attrs);
1771
+ const classAnalysis = classSource ? analyzeClassSource(classSource) : undefined;
1772
+ const classes = classAnalysis?.classes ?? [];
1773
+ const role = (0, utils_1.readAttr)(tagInfo.attrs, 'role');
1774
+ const kind = classifyUiElement(tagInfo.originalTag, tagInfo.attrs, classes, role);
1775
+ const label = extractElementLabel(content, tagInfo);
1776
+ const layoutRole = deriveLayoutRole(tagInfo.originalTag, kind, classes, label);
1777
+ return {
1778
+ index,
1779
+ kind,
1780
+ tag: tagInfo.originalTag.toLowerCase(),
1781
+ originalTag: tagInfo.originalTag,
1782
+ ...(layoutRole ? { layoutRole } : {}),
1783
+ classes: classes.slice(0, 32),
1784
+ ...(classAnalysis?.defaultClasses.length
1785
+ ? { defaultClasses: classAnalysis.defaultClasses.slice(0, 32) }
1786
+ : {}),
1787
+ ...(classAnalysis?.conditionalClasses.length
1788
+ ? { conditionalClasses: classAnalysis.conditionalClasses.slice(0, 32) }
1789
+ : {}),
1790
+ ...(label ? { label } : {}),
1791
+ };
1792
+ })
1793
+ .filter((child) => child.kind !== 'unknown' || child.classes.length || child.label)
1794
+ .slice(0, 16);
1795
+ }
1796
+ function deriveLayoutRole(tag, kind, classes, label) {
1797
+ const tagName = tag.toLowerCase();
1798
+ const tagBase = tag.split('.')[0].split(':').pop() ?? tag;
1799
+ const lowerBase = tagBase.toLowerCase();
1800
+ const classSet = new Set(classes);
1801
+ const classHaystack = classes.join(' ').toLowerCase();
1802
+ const labelHaystack = (label ?? '').toLowerCase();
1803
+ if (lowerBase === 'container') {
1804
+ return 'container';
1805
+ }
1806
+ if (lowerBase === 'cardheader') {
1807
+ return 'card-header-slot';
1808
+ }
1809
+ if (lowerBase === 'cardcontent') {
1810
+ return 'card-content-slot';
1811
+ }
1812
+ if (lowerBase === 'cardfooter') {
1813
+ return 'card-footer-slot';
1814
+ }
1815
+ if (lowerBase === 'cardtitle') {
1816
+ return 'card-title-slot';
1817
+ }
1818
+ if (lowerBase === 'carddescription') {
1819
+ return 'card-description-slot';
1820
+ }
1821
+ if (tagName === 'h1') {
1822
+ return 'page-heading';
1823
+ }
1824
+ if (/^h[2-6]$/.test(tagName)) {
1825
+ return kind === 'heading' && classSet.has('text-xl')
1826
+ ? 'card-or-section-heading'
1827
+ : 'section-heading';
1828
+ }
1829
+ if (kind === 'badge' ||
1830
+ classSet.has('uppercase') ||
1831
+ /\beyebrow\b/.test(classHaystack) ||
1832
+ /\b(product context|platform|workflow|plans|contact|faq|final cta)\b/.test(labelHaystack)) {
1833
+ return 'eyebrow-or-badge';
1834
+ }
1835
+ if (classSet.has('section-frame') || classSet.has('sm:section-frame')) {
1836
+ return 'framed-section-shell';
1837
+ }
1838
+ if (classSet.has('context-surface')) {
1839
+ return 'technical-context-surface';
1840
+ }
1841
+ if (tagName === 'section' || lowerBase === 'section') {
1842
+ return 'page-section';
1843
+ }
1844
+ if (classSet.has('grid') ||
1845
+ classes.some((className) => (0, utils_1.classBase)(className).startsWith('grid-cols-'))) {
1846
+ return 'grid-layout';
1847
+ }
1848
+ if (classSet.has('flex') || classSet.has('inline-flex')) {
1849
+ return 'flex-layout';
1850
+ }
1851
+ if (classes.some((className) => (0, utils_1.classBase)(className).startsWith('space-y-'))) {
1852
+ return 'stack-layout';
1853
+ }
1854
+ if (tagName === 'p' || tagName === 'span' || tagName === 'small' || tagName === 'code') {
1855
+ return 'text-copy';
1856
+ }
1857
+ return undefined;
1858
+ }
1859
+ function isStateClass(className) {
1860
+ return /^(?:hover|focus|active|disabled|visited|dark|group|peer|aria|data):/.test(className);
1861
+ }
1862
+ function extractApiCalls(content) {
1863
+ const calls = new Set();
1864
+ for (const pattern of [
1865
+ /\bfetch\(\s*['"`]([^'"`]+)['"`]/g,
1866
+ /\baxios\.(?:get|post|put|patch|delete)\(\s*['"`]([^'"`]+)['"`]/g,
1867
+ /\b(?:get|post|put|patch|delete)Json\(\s*['"`]([^'"`]+)['"`]/g,
1868
+ /\$\.ajax\(\s*\{[\s\S]*?url\s*:\s*['"`]([^'"`]+)['"`]/g,
1869
+ ]) {
1870
+ for (const match of content.matchAll(pattern)) {
1871
+ const value = match[1].trim();
1872
+ if (value.startsWith('/')) {
1873
+ calls.add((0, utils_1.stripQuery)(value));
1874
+ }
1875
+ }
1876
+ }
1877
+ return Array.from(calls).sort();
1878
+ }
1879
+ function extractEnvVars(content) {
1880
+ return (0, utils_1.unique)(Array.from(content.matchAll(/(?:process\.env\.|process\.env\[['"]|os\.environ\[['"]|ENV\[['"]|getenv\(['"]|env\(['"]|import\.meta\.env\.)([A-Z][A-Z0-9_]*)/g)).map((match) => match[1])).slice(0, 80);
1881
+ }
1882
+ function extractTestNames(content, relPath) {
1883
+ if (!isTestFile(relPath)) {
1884
+ return [];
1885
+ }
1886
+ const names = new Set();
1887
+ for (const match of content.matchAll(/\b(?:describe|it|test|specify)\(\s*['"`]([^'"`]+)['"`]/g)) {
1888
+ names.add(match[1].slice(0, 120));
1889
+ }
1890
+ for (const match of content.matchAll(/^\s*def\s+(test_[A-Za-z0-9_]+)/gm)) {
1891
+ names.add(match[1]);
1892
+ }
1893
+ return Array.from(names).sort().slice(0, 80);
1894
+ }
1895
+ function extractDocsHeadings(content, relPath) {
1896
+ if (!/\.(md|mdx)$/i.test(relPath)) {
1897
+ return [];
1898
+ }
1899
+ return Array.from(content.matchAll(/^#{1,4}\s+(.+)$/gm))
1900
+ .map((match) => match[1].trim())
1901
+ .slice(0, 80);
1902
+ }
1903
+ function extractConfigKeys(relPath, content) {
1904
+ const baseName = path.basename(relPath);
1905
+ if (!isConfigFile(relPath)) {
1906
+ return [];
1907
+ }
1908
+ if (/\.(json)$/.test(relPath) || baseName === 'composer.json' || baseName === 'package.json') {
1909
+ try {
1910
+ const parsed = JSON.parse(content);
1911
+ return Object.keys(parsed).slice(0, 80);
1912
+ }
1913
+ catch {
1914
+ return [];
1915
+ }
1916
+ }
1917
+ if (/\.(yml|yaml|toml)$/.test(relPath) ||
1918
+ ['Gemfile', 'go.mod', 'requirements.txt'].includes(baseName)) {
1919
+ return (0, utils_1.unique)(content
1920
+ .split('\n')
1921
+ .map((line) => line.trim().match(/^([A-Za-z0-9_.-]+)\s*[:=]/)?.[1])
1922
+ .filter((item) => Boolean(item))).slice(0, 80);
1923
+ }
1924
+ return Array.from(content.matchAll(/\b([A-Za-z_$][\w$]*)\s*[:=]/g))
1925
+ .map((match) => match[1])
1926
+ .slice(0, 80);
1927
+ }
1928
+ function extractTemplateIncludes(content, relPath) {
1929
+ if (!isTemplateFile(relPath) && !/\.(html|htm|php|phtml)$/.test(relPath)) {
1930
+ return [];
1931
+ }
1932
+ const includes = new Set();
1933
+ for (const pattern of [
1934
+ /@include\(\s*['"]([^'"]+)['"]/g,
1935
+ /\b(?:include|require|include_once|require_once)\s*(?:\(?\s*)['"]([^'"]+)['"]/g,
1936
+ /\{%\s*(?:include|extends|embed)\s+['"]([^'"]+)['"]/g,
1937
+ /<%-?\s*include\(\s*['"]([^'"]+)['"]/g,
1938
+ /{{>\s*([A-Za-z0-9_./-]+)/g,
1939
+ ]) {
1940
+ for (const match of content.matchAll(pattern)) {
1941
+ includes.add(match[1]);
1942
+ }
1943
+ }
1944
+ return Array.from(includes).sort();
1945
+ }
1946
+ function extractAuthHints(content) {
1947
+ const hints = new Set();
1948
+ for (const hint of [
1949
+ 'auth',
1950
+ 'authenticated',
1951
+ 'authorize',
1952
+ 'permission',
1953
+ 'role',
1954
+ 'guard',
1955
+ 'session',
1956
+ 'csrf',
1957
+ 'jwt',
1958
+ 'token',
1959
+ ]) {
1960
+ if (new RegExp(`\\b${hint}\\b`, 'i').test(content)) {
1961
+ hints.add(hint);
1962
+ }
1963
+ }
1964
+ return Array.from(hints).sort();
1965
+ }
1966
+ function extractMiddleware(content, relPath) {
1967
+ const names = new Set();
1968
+ if (/middleware/i.test(relPath)) {
1969
+ names.add(path.basename(relPath).replace(/\.[^.]+$/, ''));
1970
+ }
1971
+ for (const match of content.matchAll(/\b(?:middleware|use|before_action|before_filter)\(\s*['"]?([A-Za-z_][\w:.-]*)/g)) {
1972
+ names.add(match[1]);
1973
+ }
1974
+ for (const match of content.matchAll(/class\s+([A-Z][A-Za-z0-9_]*Middleware)\b/g)) {
1975
+ names.add(match[1]);
1976
+ }
1977
+ return Array.from(names).sort();
1978
+ }
1979
+ function extractJobs(content, relPath) {
1980
+ const jobs = new Set();
1981
+ for (const className of extractClasses(content, relPath)) {
1982
+ if (/(Job|Worker|Task|Command|Cron|Queue)$/.test(className)) {
1983
+ jobs.add(className);
1984
+ }
1985
+ }
1986
+ for (const match of content.matchAll(/\b(?:queue|dispatch|enqueue|schedule|cron)\(['"`]?([A-Za-z0-9_.:-]+)?/g)) {
1987
+ if (match[1]) {
1988
+ jobs.add(match[1]);
1989
+ }
1990
+ }
1991
+ return Array.from(jobs).sort();
1992
+ }
1993
+ function extractEvents(content) {
1994
+ const events = new Set();
1995
+ for (const match of content.matchAll(/\b(?:emit|dispatch|publish|subscribe|on)\(\s*['"`]([^'"`]+)['"`]/g)) {
1996
+ events.add(match[1]);
1997
+ }
1998
+ return Array.from(events).sort().slice(0, 80);
1999
+ }
2000
+ function detectKind(relPath, content) {
2001
+ const baseName = path.basename(relPath);
2002
+ if (baseName === 'package.json') {
2003
+ return 'package';
2004
+ }
2005
+ if (isConfigFile(relPath)) {
2006
+ return 'config';
2007
+ }
2008
+ if (isTestFile(relPath)) {
2009
+ return 'test';
2010
+ }
2011
+ if (/\.(md|mdx)$/i.test(relPath)) {
2012
+ return 'doc';
2013
+ }
2014
+ if (isNextPageFile(relPath)) {
2015
+ return 'next-page';
2016
+ }
2017
+ if (isNextLayoutFile(relPath)) {
2018
+ return 'next-layout';
2019
+ }
2020
+ if (isNextRouteFile(relPath)) {
2021
+ return 'next-route-handler';
2022
+ }
2023
+ if (isPagesRouterPageFile(relPath)) {
2024
+ return 'pages-route';
2025
+ }
2026
+ if (isRawPageFile(relPath)) {
2027
+ return 'web-page';
2028
+ }
2029
+ if (/\.module\.(css|scss)$/.test(relPath)) {
2030
+ return 'css-module';
2031
+ }
2032
+ if (/\.(css|scss)$/.test(relPath) || /tailwind\.config\./.test(relPath)) {
2033
+ return 'stylesheet';
2034
+ }
2035
+ if (/schema\.prisma$/.test(relPath) || /\.(sql|graphql)$/.test(relPath)) {
2036
+ return 'database-schema';
2037
+ }
2038
+ if (/@Controller\(/.test(content)) {
2039
+ return 'backend-controller';
2040
+ }
2041
+ if (/\bclass\s+\w+Controller\b/.test(content) || /Controller\.php$/.test(relPath)) {
2042
+ return 'backend-controller';
2043
+ }
2044
+ if (/(?:export\s+)?class\s+\w+(Service|Repository|Client|Gateway|Provider|Manager)\b/.test(content)) {
2045
+ return 'backend-service';
2046
+ }
2047
+ if (/(?:export\s+)?class\s+\w+(Module|Package|Bundle)\b/.test(content)) {
2048
+ return 'backend-module';
2049
+ }
2050
+ if (/\b(?:router|app|server|Route::)\.(?:get|post|put|patch|delete)|Route::(?:get|post|put|patch|delete)/i.test(content)) {
2051
+ return 'api-route';
2052
+ }
2053
+ if (/\.(tsx|jsx|vue|svelte|astro)$/.test(relPath)) {
2054
+ return 'ui-component';
2055
+ }
2056
+ if (isTemplateFile(relPath)) {
2057
+ return 'template';
2058
+ }
2059
+ return 'source';
2060
+ }
2061
+ function detectLayer(relPath, kind) {
2062
+ if (kind === 'doc') {
2063
+ return 'docs';
2064
+ }
2065
+ if (kind === 'test') {
2066
+ return 'tests';
2067
+ }
2068
+ if (kind === 'config') {
2069
+ return 'config';
2070
+ }
2071
+ if (kind.startsWith('backend') ||
2072
+ relPath.startsWith('prisma/') ||
2073
+ relPath.startsWith('database/') ||
2074
+ relPath.startsWith('db/')) {
2075
+ return 'backend';
2076
+ }
2077
+ if (kind.startsWith('next') ||
2078
+ kind.includes('component') ||
2079
+ relPath.includes('/components/') ||
2080
+ relPath.startsWith('components/')) {
2081
+ return 'frontend';
2082
+ }
2083
+ if (kind.includes('page') || kind === 'template' || isTemplateFile(relPath)) {
2084
+ return 'frontend';
2085
+ }
2086
+ if (kind.includes('style') || kind.includes('css')) {
2087
+ return 'style';
2088
+ }
2089
+ if (relPath.includes('/api/') || relPath.startsWith('api/')) {
2090
+ return 'api';
2091
+ }
2092
+ return 'shared';
2093
+ }
2094
+ function detectLanguage(filePath) {
2095
+ const normalized = (0, utils_1.toPosixPath)(filePath).toLowerCase();
2096
+ if (normalized.endsWith('.blade.php')) {
2097
+ return 'blade.php';
2098
+ }
2099
+ if (normalized.endsWith('.module.scss')) {
2100
+ return 'scss-module';
2101
+ }
2102
+ if (normalized.endsWith('.module.css')) {
2103
+ return 'css-module';
2104
+ }
2105
+ const extension = path.extname(filePath).replace('.', '');
2106
+ return extension || path.basename(filePath);
2107
+ }
2108
+ function summarizeFile(input) {
2109
+ if (input.routes.length) {
2110
+ return `Frontend route ${input.routes.join(', ')}.`;
2111
+ }
2112
+ if (input.apiHandlers.length) {
2113
+ return `API handlers: ${input.apiHandlers.join(', ')}.`;
2114
+ }
2115
+ if (input.components.length) {
2116
+ return `UI components: ${input.components.join(', ')}.`;
2117
+ }
2118
+ if (input.controllers.length) {
2119
+ return `Backend controllers: ${input.controllers.join(', ')}.`;
2120
+ }
2121
+ if (input.services.length) {
2122
+ return `Backend services: ${input.services.join(', ')}.`;
2123
+ }
2124
+ if (input.modules.length) {
2125
+ return `Backend modules: ${input.modules.join(', ')}.`;
2126
+ }
2127
+ if (input.models.length) {
2128
+ return `Data models: ${input.models.join(', ')}.`;
2129
+ }
2130
+ if (input.hooks.length) {
2131
+ return `Hooks: ${input.hooks.join(', ')}.`;
2132
+ }
2133
+ if (input.dependencies.length) {
2134
+ return `Package manifest with ${input.dependencies.length} dependencies.`;
2135
+ }
2136
+ if (input.uiElements.length) {
2137
+ const kinds = (0, utils_1.unique)(input.uiElements.map((element) => element.kind));
2138
+ return `Template UI surface with ${kinds.join(', ')} elements.`;
2139
+ }
2140
+ if (input.tests.length) {
2141
+ return `Test file with ${input.tests.length} discovered test cases.`;
2142
+ }
2143
+ if (input.docsHeadings.length) {
2144
+ return `Documentation file with headings: ${input.docsHeadings.slice(0, 3).join(', ')}.`;
2145
+ }
2146
+ if (input.configKeys.length) {
2147
+ return `Config file with keys: ${input.configKeys.slice(0, 5).join(', ')}.`;
2148
+ }
2149
+ if (input.symbols.length) {
2150
+ return `Source file with symbols: ${input.symbols.slice(0, 5).join(', ')}.`;
2151
+ }
2152
+ return `${input.kind} file: ${input.relPath}.`;
2153
+ }
2154
+ function isNextPageFile(relPath) {
2155
+ const parts = (0, utils_1.toPosixPath)(relPath).split('/');
2156
+ return parts.includes('app') && /^page\.(tsx|jsx|ts|js)$/.test(parts[parts.length - 1]);
2157
+ }
2158
+ function isNextLayoutFile(relPath) {
2159
+ const parts = (0, utils_1.toPosixPath)(relPath).split('/');
2160
+ return parts.includes('app') && /^layout\.(tsx|jsx|ts|js)$/.test(parts[parts.length - 1]);
2161
+ }
2162
+ function isNextRouteFile(relPath) {
2163
+ const parts = (0, utils_1.toPosixPath)(relPath).split('/');
2164
+ return parts.includes('app') && /^route\.(tsx|jsx|ts|js)$/.test(parts[parts.length - 1]);
2165
+ }
2166
+ function isPagesRouterPageFile(relPath) {
2167
+ const parts = (0, utils_1.toPosixPath)(relPath).split('/');
2168
+ const pagesIndex = parts.lastIndexOf('pages');
2169
+ const fileName = parts[parts.length - 1];
2170
+ return pagesIndex >= 0 && !parts.includes('api') && /\.(tsx|jsx|ts|js|vue)$/.test(fileName);
2171
+ }
2172
+ function isSvelteKitPageFile(relPath) {
2173
+ return /(?:^|\/)routes\/.*\/\+page\.svelte$/.test((0, utils_1.toPosixPath)(relPath));
2174
+ }
2175
+ function isAstroPageFile(relPath) {
2176
+ return /(?:^|\/)pages\/.*\.astro$/.test((0, utils_1.toPosixPath)(relPath));
2177
+ }
2178
+ function isNuxtPageFile(relPath) {
2179
+ return /(?:^|\/)pages\/.*\.(vue|ts|js)$/.test((0, utils_1.toPosixPath)(relPath));
2180
+ }
2181
+ function isRawPageFile(relPath) {
2182
+ const normalized = (0, utils_1.toPosixPath)(relPath);
2183
+ if (!/\.(html|htm|php|phtml)$/.test(normalized)) {
2184
+ return false;
2185
+ }
2186
+ return !/(?:^|\/)(partials?|includes?|components?|layouts?|templates\/partials)\//.test(normalized);
2187
+ }
2188
+ function toNextRoutePath(relPath) {
2189
+ const parts = (0, utils_1.toPosixPath)(relPath).split('/');
2190
+ const appIndex = parts.lastIndexOf('app');
2191
+ const routeParts = parts
2192
+ .slice(appIndex + 1, -1)
2193
+ .filter((part) => part && !part.startsWith('(') && !part.startsWith('@'))
2194
+ .map((part) => part.startsWith('[') && part.endsWith(']')
2195
+ ? `:${part.slice(1, -1).replace('...', '')}`
2196
+ : part);
2197
+ return `/${routeParts.join('/')}`.replace(/\/+$/, '') || '/';
2198
+ }
2199
+ function toPagesRoutePath(relPath) {
2200
+ const parts = (0, utils_1.toPosixPath)(relPath).split('/');
2201
+ const pagesIndex = parts.lastIndexOf('pages');
2202
+ const routeParts = parts
2203
+ .slice(pagesIndex + 1)
2204
+ .join('/')
2205
+ .replace(/\.(tsx|jsx|ts|js|vue)$/, '')
2206
+ .split('/')
2207
+ .filter((part) => part !== 'index' && !part.startsWith('_'))
2208
+ .map((part) => part.startsWith('[') && part.endsWith(']')
2209
+ ? `:${part.slice(1, -1).replace('...', '')}`
2210
+ : part);
2211
+ return `/${routeParts.join('/')}`.replace(/\/+$/, '') || '/';
2212
+ }
2213
+ function toSvelteKitRoutePath(relPath) {
2214
+ const parts = (0, utils_1.toPosixPath)(relPath).split('/');
2215
+ const routesIndex = parts.lastIndexOf('routes');
2216
+ const routeParts = parts
2217
+ .slice(routesIndex + 1, -1)
2218
+ .filter((part) => part && !part.startsWith('('))
2219
+ .map((part) => (part.startsWith('[') && part.endsWith(']') ? `:${part.slice(1, -1)}` : part));
2220
+ return `/${routeParts.join('/')}`.replace(/\/+$/, '') || '/';
2221
+ }
2222
+ function toAstroRoutePath(relPath) {
2223
+ const parts = (0, utils_1.toPosixPath)(relPath).split('/');
2224
+ const pagesIndex = parts.lastIndexOf('pages');
2225
+ const routeParts = parts
2226
+ .slice(pagesIndex + 1)
2227
+ .join('/')
2228
+ .replace(/\.astro$/, '')
2229
+ .split('/')
2230
+ .filter((part) => part !== 'index')
2231
+ .map((part) => (part.startsWith('[') && part.endsWith(']') ? `:${part.slice(1, -1)}` : part));
2232
+ return `/${routeParts.join('/')}`.replace(/\/+$/, '') || '/';
2233
+ }
2234
+ function toNuxtRoutePath(relPath) {
2235
+ return toPagesRoutePath(relPath).replace(/\/_/g, '/:');
2236
+ }
2237
+ function toRawPageRoutePath(relPath) {
2238
+ const normalized = (0, utils_1.toPosixPath)(relPath);
2239
+ const withoutExtension = normalized.replace(/\.(html|htm|php|phtml)$/, '');
2240
+ const route = withoutExtension
2241
+ .replace(/^(public|static|web|src|app)\//, '')
2242
+ .replace(/^resources\/views\//, '')
2243
+ .replace(/^views\//, '')
2244
+ .replace(/^templates\//, '')
2245
+ .replace(/\/index$/, '')
2246
+ .replace(/^index$/, '');
2247
+ return normalizeRoutePath(route);
2248
+ }
2249
+ function extractFrameworkRoutePaths(relPath, content) {
2250
+ const routes = new Set();
2251
+ for (const handler of extractApiHandlers(relPath, content)) {
2252
+ routes.add(handler.replace(/^[A-Z]+\s+/, ''));
2253
+ }
2254
+ return Array.from(routes).filter(Boolean);
2255
+ }
2256
+ function normalizeRoutePath(value) {
2257
+ const route = (0, utils_1.stripQuery)(value)
2258
+ .replace(/^['"`]|['"`]$/g, '')
2259
+ .replace(/^\^/, '')
2260
+ .replace(/\$$/, '')
2261
+ .replace(/\/+$/, '')
2262
+ .replace(/^\s+|\s+$/g, '');
2263
+ return `/${route.replace(/^\/+/, '')}`.replace(/\/+$/, '') || '/';
2264
+ }
2265
+ function shouldIgnoreDirectory(relPath) {
2266
+ if (relPath === '.') {
2267
+ return false;
2268
+ }
2269
+ return relPath.split('/').some((part) => constants_1.ignoredDirNames.has(part));
2270
+ }
2271
+ function isExcluded(relPath, excludePaths, explicitIncludePaths = false) {
2272
+ const normalized = relPath === '.' ? relPath : (0, utils_1.stripLeadingDot)((0, utils_1.toPosixPath)(relPath));
2273
+ if (shouldIgnoreFileName(path.basename(normalized))) {
2274
+ return true;
2275
+ }
2276
+ if (!explicitIncludePaths && shouldIgnoreNonCanonicalPath(normalized)) {
2277
+ return true;
2278
+ }
2279
+ return excludePaths.some((excludePath) => {
2280
+ const normalizedExclude = (0, utils_1.stripLeadingDot)((0, utils_1.toPosixPath)(excludePath));
2281
+ return normalized === normalizedExclude || normalized.startsWith(`${normalizedExclude}/`);
2282
+ });
2283
+ }
2284
+ function shouldIgnoreNonCanonicalPath(relPath) {
2285
+ return (/^(?:src\/)?app\/test(?:\/|$)/i.test(relPath) ||
2286
+ /^(?:src\/)?pages\/test(?:\/|$)/i.test(relPath) ||
2287
+ /^(?:src\/)?app\/(?:playground|playgrounds|demo|demos|example|examples|sandbox)(?:\/|$)/i.test(relPath) ||
2288
+ /(?:^|\/)playwright-mcp\.page[^/]*\.(?:tsx|jsx|ts|js|html|mdx?)$/i.test(relPath));
2289
+ }
2290
+ function isScannableFile(filePath) {
2291
+ const fileName = path.basename(filePath);
2292
+ const normalized = (0, utils_1.toPosixPath)(filePath).toLowerCase();
2293
+ return (constants_1.scannableFileNames.has(fileName) ||
2294
+ constants_1.scannableExtensions.has(path.extname(filePath)) ||
2295
+ normalized.endsWith('.blade.php'));
2296
+ }
2297
+ function shouldIgnoreFileName(fileName) {
2298
+ if (constants_1.ignoredFileNames.has(fileName)) {
2299
+ return true;
2300
+ }
2301
+ if (/^\.env(?:\.|$)/.test(fileName)) {
2302
+ return true;
2303
+ }
2304
+ if (/\.(pem|key|crt|p12|pfx|keystore|jks)$/i.test(fileName)) {
2305
+ return true;
2306
+ }
2307
+ return false;
2308
+ }
2309
+ function isUiFile(relPath) {
2310
+ return /\.(tsx|jsx|vue|svelte|astro)$/.test(relPath) || isTemplateFile(relPath);
2311
+ }
2312
+ function isTemplateFile(relPath) {
2313
+ return (/\.(html|htm|php|phtml|twig|erb|ejs|hbs|handlebars|liquid|njk|pug)$/.test(relPath) ||
2314
+ relPath.endsWith('.blade.php'));
2315
+ }
2316
+ function isTestFile(relPath) {
2317
+ return (/(?:^|\/)(?:__tests__|tests?|spec)\//i.test(relPath) ||
2318
+ /\.(test|spec)\.[A-Za-z0-9]+$/.test(relPath));
2319
+ }
2320
+ function isConfigFile(relPath) {
2321
+ const baseName = path.basename(relPath);
2322
+ return (constants_1.rootConfigFiles.includes(baseName) ||
2323
+ constants_1.scannableFileNames.has(baseName) ||
2324
+ /(?:^|\/)\.github\/workflows\/.*\.ya?ml$/.test(relPath) ||
2325
+ /(?:^|\/)(?:docker-compose|compose)\.ya?ml$/.test(relPath) ||
2326
+ /(?:^|\/)(?:config|configs?)\/.+\.(json|ya?ml|toml|xml)$/.test(relPath) ||
2327
+ /\.(config|conf)\.(js|mjs|ts|json|ya?ml)$/.test(relPath));
2328
+ }
2329
+ function routeExtractorForFile(file) {
2330
+ if (file.kind.startsWith('next')) {
2331
+ return 'nextjs.app-router';
2332
+ }
2333
+ if (file.kind === 'pages-route') {
2334
+ return 'nextjs.pages-router';
2335
+ }
2336
+ if (file.language === 'svelte') {
2337
+ return 'sveltekit';
2338
+ }
2339
+ if (file.language === 'astro') {
2340
+ return 'astro';
2341
+ }
2342
+ if (file.language === 'vue') {
2343
+ return 'vue-nuxt';
2344
+ }
2345
+ if (file.language === 'php' || file.language === 'blade.php') {
2346
+ return 'php-web';
2347
+ }
2348
+ if (file.language === 'py') {
2349
+ return 'python-web';
2350
+ }
2351
+ if (file.language === 'rb') {
2352
+ return 'ruby-web';
2353
+ }
2354
+ return 'web.routes';
2355
+ }
2356
+ function routeConfidenceForFile(file) {
2357
+ return file.kind.startsWith('next') || file.kind === 'pages-route' || file.kind === 'web-page'
2358
+ ? 'high'
2359
+ : 'medium';
2360
+ }
2361
+ function apiExtractorForFile(file) {
2362
+ if (file.kind.includes('next')) {
2363
+ return 'nextjs.route-handler';
2364
+ }
2365
+ if (file.language === 'php' || file.language === 'blade.php') {
2366
+ return 'php.routes';
2367
+ }
2368
+ if (file.language === 'py') {
2369
+ return 'python.routes';
2370
+ }
2371
+ if (file.language === 'rb') {
2372
+ return 'ruby.routes';
2373
+ }
2374
+ if (file.language === 'go') {
2375
+ return 'go.routes';
2376
+ }
2377
+ return 'api.routes';
2378
+ }
2379
+ function controllerExtractorForFile(file) {
2380
+ if (file.language === 'php') {
2381
+ return 'php-controller';
2382
+ }
2383
+ if (file.language === 'rb') {
2384
+ return 'rails-controller';
2385
+ }
2386
+ if (file.language === 'py') {
2387
+ return 'python-controller';
2388
+ }
2389
+ return 'nestjs';
2390
+ }
2391
+ function serviceExtractorForFile(file) {
2392
+ if (file.language === 'php') {
2393
+ return 'php-service';
2394
+ }
2395
+ if (file.language === 'rb') {
2396
+ return 'ruby-service';
2397
+ }
2398
+ if (file.language === 'py') {
2399
+ return 'python-service';
2400
+ }
2401
+ if (file.language === 'go') {
2402
+ return 'go-service';
2403
+ }
2404
+ return 'service-convention';
2405
+ }
2406
+ function moduleExtractorForFile(file) {
2407
+ return file.language === 'ts' ? 'nestjs' : 'module-convention';
2408
+ }
2409
+ function modelExtractorForFile(file) {
2410
+ if (file.language === 'prisma') {
2411
+ return 'prisma';
2412
+ }
2413
+ if (file.language === 'sql') {
2414
+ return 'sql';
2415
+ }
2416
+ if (file.language === 'rb') {
2417
+ return 'rails-model';
2418
+ }
2419
+ if (file.language === 'py') {
2420
+ return 'django-model';
2421
+ }
2422
+ if (file.language === 'php') {
2423
+ return 'php-model';
2424
+ }
2425
+ return 'orm-model';
2426
+ }
2427
+ function styleExtractorForFile(file) {
2428
+ if (file.language === 'css' ||
2429
+ file.language === 'scss' ||
2430
+ file.language === 'css-module' ||
2431
+ file.language === 'scss-module') {
2432
+ return 'styles';
2433
+ }
2434
+ if (file.tailwindUtilities.length) {
2435
+ return 'tailwind-classes';
2436
+ }
2437
+ if (isTemplateFile(file.relPath)) {
2438
+ return 'template-styles';
2439
+ }
2440
+ return 'ui-styles';
2441
+ }
2442
+ function styleConfidenceForFile(file) {
2443
+ return file.cssClasses.length ||
2444
+ file.cssVariables.length ||
2445
+ file.cssVariableValues.length ||
2446
+ file.cssRules.length ||
2447
+ file.componentStyleDefinitions.length ||
2448
+ file.themeTokens.length ||
2449
+ file.inlineStyles.length
2450
+ ? 'high'
2451
+ : file.classReferences.length || file.classExpressions.length || file.uiElements.length
2452
+ ? 'medium'
2453
+ : 'low';
2454
+ }
2455
+ function entryMatchesConcept(entry, terms) {
2456
+ const uiElements = Array.isArray(entry.value.uiElements) ? entry.value.uiElements : [];
2457
+ const componentStyleDefinitions = Array.isArray(entry.value.componentStyleDefinitions)
2458
+ ? entry.value.componentStyleDefinitions
2459
+ : [];
2460
+ const themeTokens = Array.isArray(entry.value.themeTokens) ? entry.value.themeTokens : [];
2461
+ const haystack = [
2462
+ entry.key,
2463
+ entry.summary,
2464
+ ...entry.tags,
2465
+ String(entry.path ?? ''),
2466
+ String(entry.value.name ?? ''),
2467
+ String(entry.value.kind ?? ''),
2468
+ String(entry.value.concept ?? ''),
2469
+ Array.isArray(entry.value.classes) ? entry.value.classes.join(' ') : '',
2470
+ Array.isArray(entry.value.classReferences) ? entry.value.classReferences.join(' ') : '',
2471
+ Array.isArray(entry.value.cssClasses) ? entry.value.cssClasses.join(' ') : '',
2472
+ Array.isArray(entry.value.tailwindUtilities) ? entry.value.tailwindUtilities.join(' ') : '',
2473
+ Array.isArray(entry.value.cssVariables) ? entry.value.cssVariables.join(' ') : '',
2474
+ Array.isArray(entry.value.cssVariableReferences)
2475
+ ? entry.value.cssVariableReferences.join(' ')
2476
+ : '',
2477
+ ...componentStyleDefinitions.map((definition) => typeof definition === 'object' && definition !== null && 'name' in definition
2478
+ ? String(definition.name)
2479
+ : ''),
2480
+ ...themeTokens.map((token) => typeof token === 'object' && token !== null && 'path' in token ? String(token.path) : ''),
2481
+ ...uiElements.flatMap((element) => {
2482
+ if (typeof element !== 'object' || element === null) {
2483
+ return [];
2484
+ }
2485
+ const value = element;
2486
+ return [
2487
+ String(value.kind ?? ''),
2488
+ String(value.tag ?? ''),
2489
+ String(value.originalTag ?? ''),
2490
+ Array.isArray(value.classes) ? value.classes.join(' ') : '',
2491
+ ];
2492
+ }),
2493
+ ]
2494
+ .join(' ')
2495
+ .toLowerCase();
2496
+ return terms.some((term) => {
2497
+ const normalized = term.toLowerCase();
2498
+ return (new RegExp(`\\b${(0, utils_1.escapeRegExp)(normalized)}s?\\b`).test(haystack) ||
2499
+ haystack.includes(normalized));
2500
+ });
2501
+ }
2502
+ function splitClassList(value) {
2503
+ return (0, utils_1.unique)(value
2504
+ .replace(/\{[^}]*\}/g, ' ')
2505
+ .replace(/\$\([^)]*\)/g, ' ')
2506
+ .split(/\s+/)
2507
+ .map((item) => item
2508
+ .trim()
2509
+ .replace(/^['"`]+|['"`]+$/g, '')
2510
+ .replace(/,$/, ''))
2511
+ .filter((item) => Boolean(item) &&
2512
+ item.length <= 180 &&
2513
+ !/[{};'"]/.test(item) &&
2514
+ !item.includes('=') &&
2515
+ !item.endsWith(':') &&
2516
+ !/(?:^|\/)<\/?[A-Za-z]/.test(item) &&
2517
+ (!/[<>]/.test(item) || item.includes('[')) &&
2518
+ (!/[()]/.test(item) || item.includes('[')) &&
2519
+ !/^(?:const|let|var|return|class|className|defaultVariants|variants|variant|size|true|false|null|undefined|map|item|props|children|function)$/.test(item) &&
2520
+ !constants_1.classTokenStopWords.has(item.toLowerCase()) &&
2521
+ !/^[A-Z][a-z]+$/.test(item) &&
2522
+ /[A-Za-z0-9\]]/.test(item)));
2523
+ }
2524
+ function classifyUiElement(tag, attrs, classes, role) {
2525
+ const originalTag = tag;
2526
+ const tagName = originalTag.toLowerCase();
2527
+ const tagBase = originalTag.split('.')[0].split(':').pop() ?? originalTag;
2528
+ const lowerBase = tagBase.toLowerCase();
2529
+ const classHaystack = [tagName, role ?? '', ...classes].join(' ').toLowerCase();
2530
+ const propHaystack = [
2531
+ tagName,
2532
+ role ?? '',
2533
+ (0, utils_1.readAttr)(attrs, 'variant') ?? '',
2534
+ (0, utils_1.readAttr)(attrs, 'type') ?? '',
2535
+ ]
2536
+ .join(' ')
2537
+ .toLowerCase();
2538
+ if (tagName === 'form' || lowerBase === 'form') {
2539
+ return 'form';
2540
+ }
2541
+ if (/^h[1-6]$/.test(tagName) || /^(heading|title|headline)$/i.test(tagBase)) {
2542
+ return 'heading';
2543
+ }
2544
+ if (['input', 'textarea', 'select', 'option', 'label', 'fieldset'].includes(tagName) ||
2545
+ /^(input|textarea|select|field|fieldlabel|label|checkbox|switch|slider|radio|combobox)$/i.test(tagBase) ||
2546
+ /\b(field|input|textarea|select|checkbox|switch|slider|radio)\b/.test(classHaystack)) {
2547
+ return 'input';
2548
+ }
2549
+ if (tagName === 'button' ||
2550
+ role === 'button' ||
2551
+ /^(button|iconbutton|submitbutton|cta)$/i.test(tagBase) ||
2552
+ /\b(btn|button|cta|submit)\b/.test(classHaystack) ||
2553
+ /\bsubmit\b/.test(propHaystack)) {
2554
+ return 'button';
2555
+ }
2556
+ if (tagName === 'a' ||
2557
+ role === 'link' ||
2558
+ /^(link|navlink|menulink)$/i.test(tagBase) ||
2559
+ /\blink\b/.test(classHaystack)) {
2560
+ return 'link';
2561
+ }
2562
+ if (tagName === 'nav' ||
2563
+ role === 'navigation' ||
2564
+ /^(nav|navbar|navigation|menu|menubar|breadcrumb|sidebar)$/i.test(tagBase) ||
2565
+ /\b(nav|navbar|navigation|menu|sidebar|breadcrumb)\b/.test(classHaystack)) {
2566
+ return 'nav';
2567
+ }
2568
+ if (tagName === 'table' || ['thead', 'tbody', 'tfoot', 'tr', 'td', 'th'].includes(tagName)) {
2569
+ return 'table';
2570
+ }
2571
+ if (tagName === 'dialog' ||
2572
+ role === 'dialog' ||
2573
+ /^(dialog|modal|popover|drawer|sheet|alertdialog)$/i.test(tagBase) ||
2574
+ /\b(dialog|modal|popover|drawer|sheet)\b/.test(classHaystack)) {
2575
+ return 'dialog';
2576
+ }
2577
+ if (/^(badge|chip|pill|tag|status|eyebrow)$/i.test(tagBase) ||
2578
+ /\b(badge|chip|pill|tag|status|eyebrow)\b/.test(classHaystack)) {
2579
+ return 'badge';
2580
+ }
2581
+ if (/^(card|panel|tile|surface|sectionframe|frame|callout)$/i.test(tagBase) ||
2582
+ /\b(card|panel|tile|surface|section-frame|frame|callout)\b/.test(classHaystack)) {
2583
+ return 'card';
2584
+ }
2585
+ if (tagName === 'section' || lowerBase === 'section') {
2586
+ return 'section';
2587
+ }
2588
+ if (['p', 'span', 'small', 'strong', 'em', 'code', 'pre', 'li'].includes(tagName) &&
2589
+ classes.some((className) => isTypographyClass(className) || isSpacingClass(className))) {
2590
+ return 'text';
2591
+ }
2592
+ if (classes.some((className) => /^(?:grid|flex|inline-flex|block|hidden|relative|absolute|mx-auto|max-w-|min-w-|w-full|gap-|space-y-|items-|justify-)/.test((0, utils_1.classBase)(className)))) {
2593
+ return 'layout';
2594
+ }
2595
+ return 'unknown';
2596
+ }
2597
+ function stripTags(value) {
2598
+ return value.replace(/<[^>]+>/g, ' ').replace(/\{[{%][\s\S]*?[}%]\}/g, ' ');
2599
+ }
2600
+ //# sourceMappingURL=extractors.js.map