code-auditor-mcp 3.4.9 → 3.4.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +10 -3
- package/CHANGELOG.md +81 -0
- package/dist/analyzers/analyzerUtils.d.ts.map +1 -1
- package/dist/analyzers/analyzerUtils.js +2 -1
- package/dist/analyzers/analyzerUtils.js.map +1 -1
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts +0 -2
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js +74 -99
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js.map +1 -1
- package/dist/analyzers/documentationAnalyzer.d.ts +1 -5
- package/dist/analyzers/documentationAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/documentationAnalyzer.js +13 -69
- package/dist/analyzers/documentationAnalyzer.js.map +1 -1
- package/dist/analyzers/invariantsAnalyzer.d.ts +1 -6
- package/dist/analyzers/invariantsAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/invariantsAnalyzer.js +16 -41
- package/dist/analyzers/invariantsAnalyzer.js.map +1 -1
- package/dist/analyzers/reactAnalyzer.d.ts +20 -3
- package/dist/analyzers/reactAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/reactAnalyzer.js +4 -165
- package/dist/analyzers/reactAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.js +43 -69
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts +62 -0
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js +357 -3
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts +81 -69
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.js +319 -325
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalStylesAnalyzer.js +32 -37
- package/dist/analyzers/universal/UniversalStylesAnalyzer.js.map +1 -1
- package/dist/auditRunner.d.ts +2 -7
- package/dist/auditRunner.d.ts.map +1 -1
- package/dist/auditRunner.js +306 -434
- package/dist/auditRunner.js.map +1 -1
- package/dist/cli.js +27 -5
- package/dist/cli.js.map +1 -1
- package/dist/codeIndexDB.d.ts +22 -0
- package/dist/codeIndexDB.d.ts.map +1 -1
- package/dist/codeIndexDB.js +92 -44
- package/dist/codeIndexDB.js.map +1 -1
- package/dist/config/defaults.js +3 -3
- package/dist/config/defaults.js.map +1 -1
- package/dist/conventions/conventionMiner.d.ts +5 -4
- package/dist/conventions/conventionMiner.d.ts.map +1 -1
- package/dist/conventions/conventionMiner.js +65 -25
- package/dist/conventions/conventionMiner.js.map +1 -1
- package/dist/dataPaths.js +1 -1
- package/dist/dataPaths.js.map +1 -1
- package/dist/invariants/index.d.ts +1 -1
- package/dist/invariants/index.d.ts.map +1 -1
- package/dist/invariants/index.js +1 -1
- package/dist/invariants/index.js.map +1 -1
- package/dist/invariants/ruleEngine.d.ts +38 -3
- package/dist/invariants/ruleEngine.d.ts.map +1 -1
- package/dist/invariants/ruleEngine.js +42 -174
- package/dist/invariants/ruleEngine.js.map +1 -1
- package/dist/languages/UniversalAnalyzer.d.ts +2 -2
- package/dist/languages/UniversalAnalyzer.d.ts.map +1 -1
- package/dist/languages/UniversalAnalyzer.js +3 -1
- package/dist/languages/UniversalAnalyzer.js.map +1 -1
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts +3 -0
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts.map +1 -1
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js +60 -16
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js.map +1 -1
- package/dist/mcp-tools-shared.d.ts.map +1 -1
- package/dist/mcp-tools-shared.js +10 -6
- package/dist/mcp-tools-shared.js.map +1 -1
- package/dist/mcp.js +0 -0
- package/dist/mcpAuditJobs.d.ts.map +1 -1
- package/dist/mcpAuditJobs.js +4 -1
- package/dist/mcpAuditJobs.js.map +1 -1
- package/dist/pipeline.d.ts +74 -0
- package/dist/pipeline.d.ts.map +1 -0
- package/dist/pipeline.js +620 -0
- package/dist/pipeline.js.bak +465 -0
- package/dist/pipeline.js.bak2 +465 -0
- package/dist/pipeline.js.map +1 -0
- package/dist/pipelineAdapters.d.ts +116 -0
- package/dist/pipelineAdapters.d.ts.map +1 -0
- package/dist/pipelineAdapters.js +1146 -0
- package/dist/pipelineAdapters.js.map +1 -0
- package/dist/pipelineTypes.d.ts +44 -0
- package/dist/pipelineTypes.d.ts.map +1 -0
- package/dist/pipelineTypes.js +118 -0
- package/dist/pipelineTypes.js.map +1 -0
- package/dist/reporting/csvReportGenerator.d.ts.map +1 -1
- package/dist/reporting/csvReportGenerator.js +2 -1
- package/dist/reporting/csvReportGenerator.js.map +1 -1
- package/dist/reporting/jsonReportGenerator.d.ts.map +1 -1
- package/dist/reporting/jsonReportGenerator.js +4 -2
- package/dist/reporting/jsonReportGenerator.js.map +1 -1
- package/dist/reporting/sarifReportGenerator.d.ts.map +1 -1
- package/dist/reporting/sarifReportGenerator.js +2 -1
- package/dist/reporting/sarifReportGenerator.js.map +1 -1
- package/dist/styles/cssAstExtractor.d.ts +46 -0
- package/dist/styles/cssAstExtractor.d.ts.map +1 -0
- package/dist/styles/cssAstExtractor.js +439 -0
- package/dist/styles/cssAstExtractor.js.map +1 -0
- package/dist/styles/styleExtractor.d.ts +2 -2
- package/dist/styles/styleExtractor.d.ts.map +1 -1
- package/dist/styles/styleExtractor.js +69 -16
- package/dist/styles/styleExtractor.js.map +1 -1
- package/dist/styles/styleIndexer.d.ts.map +1 -1
- package/dist/styles/styleIndexer.js +4 -0
- package/dist/styles/styleIndexer.js.map +1 -1
- package/dist/styles/tailwindProbe.d.ts +2 -0
- package/dist/styles/tailwindProbe.d.ts.map +1 -1
- package/dist/styles/tailwindProbe.js +5 -5
- package/dist/styles/tailwindProbe.js.map +1 -1
- package/dist/styles/tailwindUtilityExpander.d.ts +3 -0
- package/dist/styles/tailwindUtilityExpander.d.ts.map +1 -1
- package/dist/styles/tailwindUtilityExpander.js +7 -0
- package/dist/styles/tailwindUtilityExpander.js.map +1 -1
- package/dist/types.d.ts +216 -11
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/fileDiscovery.d.ts +4 -0
- package/dist/utils/fileDiscovery.d.ts.map +1 -1
- package/dist/utils/fileDiscovery.js +7 -1
- package/dist/utils/fileDiscovery.js.map +1 -1
- package/dist/utils/reactDetection.d.ts.map +1 -1
- package/dist/utils/reactDetection.js +32 -0
- package/dist/utils/reactDetection.js.map +1 -1
- package/package.json +2 -4
- package/plugin/.claude-plugin/plugin.json +1 -1
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSS AST Extractor — Spec 26 Phase 2.
|
|
3
|
+
*
|
|
4
|
+
* Replaces regex-based CSS parsing (styleExtractor.ts extractFromCSS) with
|
|
5
|
+
* AST-based extraction using tree-sitter-css parsed structures.
|
|
6
|
+
*
|
|
7
|
+
* Three pure functions mirror the output of the regex extractors:
|
|
8
|
+
* 1. extractDeclarationsFromCSSAst — NormalizedDeclaration[]
|
|
9
|
+
* 2. extractTokensFromCSSAst — StyleToken[]
|
|
10
|
+
* 3. extractClassUsageFromCSSAst — StyleClassUsage[]
|
|
11
|
+
*
|
|
12
|
+
* SCSS is NOT handled here — tree-sitter-css is not an SCSS grammar.
|
|
13
|
+
* SCSS files stay on the regex path in styleExtractor.ts.
|
|
14
|
+
*/
|
|
15
|
+
import { normalizeValue, expandShorthand } from './normalizer.js';
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Helpers
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
/**
|
|
20
|
+
* Extract a CSS custom property reference from a value like `var(--token)`.
|
|
21
|
+
* Token-level classification (not structure discovery) — the regex reads a
|
|
22
|
+
* known token name from a single value string.
|
|
23
|
+
*/
|
|
24
|
+
function extractTokenRef(rawValue) {
|
|
25
|
+
const match = rawValue.match(/var\(\s*(--[a-zA-Z0-9_-]+)/);
|
|
26
|
+
return match ? match[1] : null;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Get the text content of a selectors node, stripping block comments.
|
|
30
|
+
* Bug 5 fix: CSS comments are not treated as class names (tree-sitter types
|
|
31
|
+
* comment nodes explicitly, so we filter by type).
|
|
32
|
+
*/
|
|
33
|
+
function getSelectorText(selectorsNode, sourceCode) {
|
|
34
|
+
const raw = sourceCode.slice(selectorsNode.range[0], selectorsNode.range[1]);
|
|
35
|
+
// Strip block comments — token-level text munging on AST-discovered content
|
|
36
|
+
return raw.replace(/\/\*[\s\S]*?\*\//g, '').trim();
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* CSS at-rule node types in tree-sitter-css.
|
|
40
|
+
*
|
|
41
|
+
* tree-sitter-css uses distinct named types for some at-rules (media_statement,
|
|
42
|
+
* supports_statement, keyframes_statement) and generic `at_rule` for everything
|
|
43
|
+
* else (@layer, @import, @apply, etc.).
|
|
44
|
+
*/
|
|
45
|
+
const AT_RULE_TYPES = new Set([
|
|
46
|
+
'at_rule',
|
|
47
|
+
'media_statement',
|
|
48
|
+
'supports_statement',
|
|
49
|
+
'keyframes_statement',
|
|
50
|
+
]);
|
|
51
|
+
/**
|
|
52
|
+
* Walk ancestor chain for at-rule nodes and build variant context string.
|
|
53
|
+
*/
|
|
54
|
+
function getVariantContext(node, adapter, sourceCode) {
|
|
55
|
+
const atRules = [];
|
|
56
|
+
let current = adapter.getParent(node);
|
|
57
|
+
while (current) {
|
|
58
|
+
if (AT_RULE_TYPES.has(current.type)) {
|
|
59
|
+
const text = sourceCode.slice(current.range[0], current.range[1]);
|
|
60
|
+
const braceIdx = text.indexOf('{');
|
|
61
|
+
const atRuleText = braceIdx !== -1 ? text.slice(0, braceIdx).trim() : text.trim();
|
|
62
|
+
atRules.unshift(atRuleText);
|
|
63
|
+
}
|
|
64
|
+
current = adapter.getParent(current);
|
|
65
|
+
}
|
|
66
|
+
return atRules.length > 0 ? atRules.join(', ') : null;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Extract @apply directives inside a rule_set block.
|
|
70
|
+
*
|
|
71
|
+
* @apply is a Tailwind extension, not standard CSS. tree-sitter-css may emit
|
|
72
|
+
* either ERROR nodes (when the grammar can't make sense of the token),
|
|
73
|
+
* postcss_statement nodes (when the grammar recognizes it as a PostCSS-style
|
|
74
|
+
* at-rule inside a rule set), or at_rule nodes (when parsed structurally as an
|
|
75
|
+
* at-rule with @apply keyword). All three paths produce the same
|
|
76
|
+
* NormalizedDeclaration shape.
|
|
77
|
+
*
|
|
78
|
+
* Bug 2: @apply in rule sets — both `@layer`-wrapped and bare. The old regex
|
|
79
|
+
* parser handled @apply in block text; AST requires explicit child-traversal.
|
|
80
|
+
*/
|
|
81
|
+
function extractApplyDirectives(blockNode, sourceCode, selector, variantContext, filePath, declarations) {
|
|
82
|
+
for (const child of blockNode.children ?? []) {
|
|
83
|
+
let applyText = null;
|
|
84
|
+
let line;
|
|
85
|
+
if (child.type === 'ERROR') {
|
|
86
|
+
// Legacy path: @apply unrecognised by grammar → ERROR node.
|
|
87
|
+
const errText = sourceCode.slice(child.range[0], child.range[1]).trim();
|
|
88
|
+
const match = errText.match(/^@apply\s+(.+)$/);
|
|
89
|
+
applyText = match ? match[1].trim() : null;
|
|
90
|
+
line = child.location.start.line;
|
|
91
|
+
}
|
|
92
|
+
else if (child.type === 'postcss_statement') {
|
|
93
|
+
// Modern path: tree-sitter-css recognises @apply as a PostCSS statement.
|
|
94
|
+
// The full text is "@apply <utilities>;". Strip the keyword and semicolon.
|
|
95
|
+
const fullText = sourceCode.slice(child.range[0], child.range[1]).trim();
|
|
96
|
+
applyText = fullText.replace(/^@apply\s+/, '').replace(/;?\s*$/, '');
|
|
97
|
+
line = child.location.start.line;
|
|
98
|
+
}
|
|
99
|
+
else if (child.type === 'at_rule') {
|
|
100
|
+
// tree-sitter-css v0.x: @apply parsed as at_rule with at_keyword child.
|
|
101
|
+
const atKeyword = child.children?.find(c => c.type === 'at_keyword');
|
|
102
|
+
if (!atKeyword)
|
|
103
|
+
continue;
|
|
104
|
+
const kwText = sourceCode
|
|
105
|
+
.slice(atKeyword.range[0], atKeyword.range[1])
|
|
106
|
+
.trim();
|
|
107
|
+
if (kwText !== '@apply')
|
|
108
|
+
continue;
|
|
109
|
+
// The keyword_query child holds the utility names.
|
|
110
|
+
const queryChild = child.children?.find(c => c.type === 'keyword_query');
|
|
111
|
+
applyText = queryChild
|
|
112
|
+
? sourceCode.slice(queryChild.range[0], queryChild.range[1]).trim()
|
|
113
|
+
: null;
|
|
114
|
+
line = child.location.start.line;
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (applyText && applyText.length > 0 && !applyText.includes('{')) {
|
|
120
|
+
declarations.push({
|
|
121
|
+
property: 'apply',
|
|
122
|
+
rawValue: applyText,
|
|
123
|
+
normalizedValue: { type: 'literal', value: applyText },
|
|
124
|
+
mechanism: 'css',
|
|
125
|
+
filePath,
|
|
126
|
+
line,
|
|
127
|
+
context: selector,
|
|
128
|
+
variantContext,
|
|
129
|
+
tokenRef: null,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// ---------------------------------------------------------------------------
|
|
135
|
+
// Tree-Sitter CSS Node Helpers
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
137
|
+
/**
|
|
138
|
+
* tree-sitter-css does NOT use field names (childForFieldName). Children are
|
|
139
|
+
* discovered by type via namedChild iteration on the raw TreeSitterNode.
|
|
140
|
+
* This grammar is distinct from the tree-sitter-typescript one.
|
|
141
|
+
*/
|
|
142
|
+
/**
|
|
143
|
+
* Find the first named child of a TreeSitterNode with the given type.
|
|
144
|
+
*/
|
|
145
|
+
function findNamedChild(node, type) {
|
|
146
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
147
|
+
const child = node.namedChild(i);
|
|
148
|
+
if (child?.type === type)
|
|
149
|
+
return child;
|
|
150
|
+
}
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Get property and raw value from a declaration's raw TreeSitterNode.
|
|
155
|
+
* tree-sitter-css uses `property_name` for the property and various typed
|
|
156
|
+
* children for the value (color_value, integer_value, plain_value,
|
|
157
|
+
* call_expression, etc.). We extract the property from the property_name
|
|
158
|
+
* child and the raw value from the full declaration text after the colon.
|
|
159
|
+
*/
|
|
160
|
+
function getPropertyAndValue(declRaw) {
|
|
161
|
+
const propNode = findNamedChild(declRaw, 'property_name');
|
|
162
|
+
if (!propNode)
|
|
163
|
+
return null;
|
|
164
|
+
const property = propNode.text.trim();
|
|
165
|
+
if (!property)
|
|
166
|
+
return null;
|
|
167
|
+
// Extract raw value from the full declaration text after the property + colon
|
|
168
|
+
const fullText = declRaw.text;
|
|
169
|
+
// Find the colon that separates property from value.
|
|
170
|
+
// Use indexOf on the full text — the property_name child's text tells us
|
|
171
|
+
// where the property ends, but the colon might be outside the child span
|
|
172
|
+
// (tree-sitter may or may not include it in property_name).
|
|
173
|
+
// Safe approach: scan from after the property_name child's end offset.
|
|
174
|
+
const propEndInDecl = propNode.endIndex - declRaw.startIndex;
|
|
175
|
+
const afterProp = fullText.slice(propEndInDecl);
|
|
176
|
+
const colonIdx = afterProp.indexOf(':');
|
|
177
|
+
const rawValue = colonIdx !== -1 ? afterProp.slice(colonIdx + 1).replace(/;\s*$/, '').trim() : '';
|
|
178
|
+
return { property, rawValue };
|
|
179
|
+
}
|
|
180
|
+
// ---------------------------------------------------------------------------
|
|
181
|
+
// AST Extraction Helpers
|
|
182
|
+
// ---------------------------------------------------------------------------
|
|
183
|
+
/**
|
|
184
|
+
* Create a synthetic AST from a subtree node for use with findNodes.
|
|
185
|
+
* findNodes needs an AST object, but we find nodes within subtrees.
|
|
186
|
+
*/
|
|
187
|
+
function subtreeAST(ast, root) {
|
|
188
|
+
return { root, language: ast.language, filePath: ast.filePath, errors: [] };
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Wrap a TreeSitterNode as an ASTNode for use with adapter methods that
|
|
192
|
+
* expect ASTNode input.
|
|
193
|
+
*/
|
|
194
|
+
function wrapAsASTNode(raw) {
|
|
195
|
+
return {
|
|
196
|
+
type: raw.type,
|
|
197
|
+
range: [raw.startIndex, raw.endIndex],
|
|
198
|
+
location: {
|
|
199
|
+
start: {
|
|
200
|
+
line: raw.startPosition.row + 1,
|
|
201
|
+
column: raw.startPosition.column + 1,
|
|
202
|
+
},
|
|
203
|
+
end: {
|
|
204
|
+
line: raw.endPosition.row + 1,
|
|
205
|
+
column: raw.endPosition.column + 1,
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
children: raw.namedChildren.map((c) => wrapAsASTNode(c)),
|
|
209
|
+
raw,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
// ---------------------------------------------------------------------------
|
|
213
|
+
// Public API
|
|
214
|
+
// ---------------------------------------------------------------------------
|
|
215
|
+
/**
|
|
216
|
+
* Extract normalized CSS declarations from a parsed CSS AST.
|
|
217
|
+
*
|
|
218
|
+
* Replaces the regex-based extractFromCSS() + extractRuleSets() + the
|
|
219
|
+
* declaration-extraction part of extractDeclarationsFromBlock() for .css files.
|
|
220
|
+
*
|
|
221
|
+
* Parser bugs eliminated by AST-based approach:
|
|
222
|
+
* Bug 1: Whitespace before @-rules — @-rules are at_rule nodes, always correct
|
|
223
|
+
* Bug 3: Nested @-rule closing brace — tree-sitter handles brace matching
|
|
224
|
+
* Bug 4: findSelectorStart walking past boundaries — no backward scanning needed
|
|
225
|
+
* Bug 5: CSS comments as class names — comment nodes are typed, filtered out
|
|
226
|
+
*/
|
|
227
|
+
export function extractDeclarationsFromCSSAst(ast, adapter, filePath, sourceCode) {
|
|
228
|
+
const declarations = [];
|
|
229
|
+
// Find all rule_sets
|
|
230
|
+
const ruleSets = adapter.findNodes(ast, { type: 'rule_set' });
|
|
231
|
+
for (const ruleSet of ruleSets) {
|
|
232
|
+
const rawNode = ruleSet.raw;
|
|
233
|
+
// tree-sitter-css: rule_set has named children 'selectors' and 'block'
|
|
234
|
+
const selectorsRaw = findNamedChild(rawNode, 'selectors');
|
|
235
|
+
if (!selectorsRaw)
|
|
236
|
+
continue;
|
|
237
|
+
const selectorsAst = wrapAsASTNode(selectorsRaw);
|
|
238
|
+
const selector = getSelectorText(selectorsAst, sourceCode);
|
|
239
|
+
if (!selector)
|
|
240
|
+
continue;
|
|
241
|
+
// Get variant context from at_rule ancestors
|
|
242
|
+
const variantContext = getVariantContext(ruleSet, adapter, sourceCode);
|
|
243
|
+
// Find declarations inside this rule_set
|
|
244
|
+
const subAST = subtreeAST(ast, ruleSet);
|
|
245
|
+
const declNodes = adapter.findNodes(subAST, { type: 'declaration' });
|
|
246
|
+
for (const decl of declNodes) {
|
|
247
|
+
const raw = decl.raw;
|
|
248
|
+
const pv = getPropertyAndValue(raw);
|
|
249
|
+
if (!pv)
|
|
250
|
+
continue;
|
|
251
|
+
const { property, rawValue } = pv;
|
|
252
|
+
if (!property || !rawValue)
|
|
253
|
+
continue;
|
|
254
|
+
const normalizedValue = normalizeValue(rawValue, property);
|
|
255
|
+
const expanded = expandShorthand(property, rawValue, normalizedValue);
|
|
256
|
+
const line = decl.location.start.line;
|
|
257
|
+
// Extract token ref from var() values or custom property names
|
|
258
|
+
const tokenRef = property.startsWith('--')
|
|
259
|
+
? property
|
|
260
|
+
: (rawValue.trim().startsWith('var(') ? extractTokenRef(rawValue) : null);
|
|
261
|
+
for (const exp of expanded) {
|
|
262
|
+
declarations.push({
|
|
263
|
+
property: exp.property,
|
|
264
|
+
rawValue: exp.rawValue,
|
|
265
|
+
normalizedValue: exp.normalizedValue,
|
|
266
|
+
mechanism: 'css',
|
|
267
|
+
filePath,
|
|
268
|
+
line,
|
|
269
|
+
context: selector,
|
|
270
|
+
variantContext,
|
|
271
|
+
tokenRef,
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
// Handle @apply directives (Tailwind extension)
|
|
276
|
+
const blockRaw = findNamedChild(rawNode, 'block');
|
|
277
|
+
if (blockRaw) {
|
|
278
|
+
const blockAst = wrapAsASTNode(blockRaw);
|
|
279
|
+
extractApplyDirectives(blockAst, sourceCode, selector, variantContext, filePath, declarations);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
// Handle @keyframes blocks — tree-sitter-css uses keyframes_statement with
|
|
283
|
+
// keyframe_block_list children, not rule_set.
|
|
284
|
+
const keyframeStmts = adapter.findNodes(ast, { type: 'keyframes_statement' });
|
|
285
|
+
for (const kf of keyframeStmts) {
|
|
286
|
+
const kfRaw = kf.raw;
|
|
287
|
+
const kfName = findNamedChild(kfRaw, 'keyframes_name')?.text ?? 'unknown';
|
|
288
|
+
const kfSubAST = subtreeAST(ast, kf);
|
|
289
|
+
const kfDeclNodes = adapter.findNodes(kfSubAST, { type: 'declaration' });
|
|
290
|
+
for (const decl of kfDeclNodes) {
|
|
291
|
+
// Find which keyframe_block this declaration belongs to
|
|
292
|
+
let parent = adapter.getParent(decl);
|
|
293
|
+
let kfSelector = '(unknown)';
|
|
294
|
+
while (parent && parent.type !== 'keyframes_statement') {
|
|
295
|
+
if (parent.type === 'keyframe_block') {
|
|
296
|
+
const selChild = parent.raw.namedChildren.find((c) => c.type === 'from' || c.type === 'to' || c.type === 'integer_value');
|
|
297
|
+
kfSelector = selChild?.text ?? '(unknown)';
|
|
298
|
+
break;
|
|
299
|
+
}
|
|
300
|
+
parent = adapter.getParent(parent);
|
|
301
|
+
}
|
|
302
|
+
const raw = decl.raw;
|
|
303
|
+
const pv = getPropertyAndValue(raw);
|
|
304
|
+
if (!pv)
|
|
305
|
+
continue;
|
|
306
|
+
const { property, rawValue } = pv;
|
|
307
|
+
if (!property || !rawValue)
|
|
308
|
+
continue;
|
|
309
|
+
const normalizedValue = normalizeValue(rawValue, property);
|
|
310
|
+
const expanded = expandShorthand(property, rawValue, normalizedValue);
|
|
311
|
+
const line = decl.location.start.line;
|
|
312
|
+
const tokenRef = property.startsWith('--')
|
|
313
|
+
? property
|
|
314
|
+
: (rawValue.trim().startsWith('var(') ? extractTokenRef(rawValue) : null);
|
|
315
|
+
for (const exp of expanded) {
|
|
316
|
+
declarations.push({
|
|
317
|
+
property: exp.property,
|
|
318
|
+
rawValue: exp.rawValue,
|
|
319
|
+
normalizedValue: exp.normalizedValue,
|
|
320
|
+
mechanism: 'css',
|
|
321
|
+
filePath,
|
|
322
|
+
line,
|
|
323
|
+
context: kfSelector,
|
|
324
|
+
variantContext: `@keyframes ${kfName}`,
|
|
325
|
+
tokenRef,
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
// Handle top-level declarations (not inside a rule_set or keyframe block — valid CSS)
|
|
331
|
+
const allDecls = adapter.findNodes(ast, { type: 'declaration' });
|
|
332
|
+
for (const decl of allDecls) {
|
|
333
|
+
// Skip declarations that are inside a rule_set or keyframe block (already processed above)
|
|
334
|
+
const parent = adapter.getParent(decl);
|
|
335
|
+
let ancestor = parent;
|
|
336
|
+
let inKeyframe = false;
|
|
337
|
+
while (ancestor) {
|
|
338
|
+
if (ancestor.type === 'keyframe_block') {
|
|
339
|
+
inKeyframe = true;
|
|
340
|
+
break;
|
|
341
|
+
}
|
|
342
|
+
ancestor = adapter.getParent(ancestor);
|
|
343
|
+
}
|
|
344
|
+
if (parent?.type === 'rule_set' || parent?.type === 'block' || inKeyframe)
|
|
345
|
+
continue;
|
|
346
|
+
const raw = decl.raw;
|
|
347
|
+
const pv = getPropertyAndValue(raw);
|
|
348
|
+
if (!pv)
|
|
349
|
+
continue;
|
|
350
|
+
const { property, rawValue } = pv;
|
|
351
|
+
if (!property || !rawValue)
|
|
352
|
+
continue;
|
|
353
|
+
const normalizedValue = normalizeValue(rawValue, property);
|
|
354
|
+
const expanded = expandShorthand(property, rawValue, normalizedValue);
|
|
355
|
+
const line = decl.location.start.line;
|
|
356
|
+
for (const exp of expanded) {
|
|
357
|
+
declarations.push({
|
|
358
|
+
property: exp.property,
|
|
359
|
+
rawValue: exp.rawValue,
|
|
360
|
+
normalizedValue: exp.normalizedValue,
|
|
361
|
+
mechanism: 'css',
|
|
362
|
+
filePath,
|
|
363
|
+
line,
|
|
364
|
+
context: '(top-level)',
|
|
365
|
+
variantContext: null,
|
|
366
|
+
tokenRef: property.startsWith('--')
|
|
367
|
+
? property
|
|
368
|
+
: (rawValue.trim().startsWith('var(') ? extractTokenRef(rawValue) : null),
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return declarations;
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Extract CSS custom property tokens from a parsed CSS AST.
|
|
376
|
+
*
|
|
377
|
+
* Replaces the regex-based extractTokens() for .css files.
|
|
378
|
+
* Finds declarations where the property starts with -- and extracts
|
|
379
|
+
* the token name and value.
|
|
380
|
+
*/
|
|
381
|
+
export function extractTokensFromCSSAst(ast, adapter, filePath) {
|
|
382
|
+
const tokens = [];
|
|
383
|
+
const allDecls = adapter.findNodes(ast, { type: 'declaration' });
|
|
384
|
+
for (const decl of allDecls) {
|
|
385
|
+
const raw = decl.raw;
|
|
386
|
+
const pv = getPropertyAndValue(raw);
|
|
387
|
+
if (!pv)
|
|
388
|
+
continue;
|
|
389
|
+
const { property, rawValue } = pv;
|
|
390
|
+
if (!property.startsWith('--'))
|
|
391
|
+
continue;
|
|
392
|
+
const name = property.trim();
|
|
393
|
+
const value = rawValue.trim();
|
|
394
|
+
if (name && value) {
|
|
395
|
+
tokens.push({
|
|
396
|
+
name,
|
|
397
|
+
value,
|
|
398
|
+
filePath,
|
|
399
|
+
mechanism: 'css-custom-property',
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return tokens;
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Extract CSS class usage from a parsed CSS AST.
|
|
407
|
+
*
|
|
408
|
+
* Replaces the regex-based class name extraction from selectors for .css files.
|
|
409
|
+
* Finds class_name nodes anywhere in the AST — each represents one CSS class
|
|
410
|
+
* definition. comment nodes are typed and naturally excluded (Bug 5 fix).
|
|
411
|
+
*/
|
|
412
|
+
export function extractClassUsageFromCSSAst(ast, adapter, filePath) {
|
|
413
|
+
const usage = [];
|
|
414
|
+
// class_name nodes represent CSS class selectors like .my-class
|
|
415
|
+
// They can appear inside selectors, pseudo-class arguments, etc.
|
|
416
|
+
const classNodes = adapter.findNodes(ast, { type: 'class_name' });
|
|
417
|
+
for (const node of classNodes) {
|
|
418
|
+
// Only extract class_name nodes that are children of class_selector.
|
|
419
|
+
// pseudo_class_selector (:hover, :focus, :nth-child) and
|
|
420
|
+
// pseudo_element_selector (::before, ::after) also contain class_name
|
|
421
|
+
// children — those are pseudo-classes, not CSS class definitions.
|
|
422
|
+
const parent = adapter.getParent(node);
|
|
423
|
+
if (!parent || parent.type !== 'class_selector')
|
|
424
|
+
continue;
|
|
425
|
+
const raw = node.raw;
|
|
426
|
+
const className = raw.text;
|
|
427
|
+
if (className) {
|
|
428
|
+
usage.push({
|
|
429
|
+
className,
|
|
430
|
+
filePath,
|
|
431
|
+
line: node.location.start.line,
|
|
432
|
+
mechanism: 'class',
|
|
433
|
+
unresolvable: false,
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
return usage;
|
|
438
|
+
}
|
|
439
|
+
//# sourceMappingURL=cssAstExtractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cssAstExtractor.js","sourceRoot":"","sources":["../../src/styles/cssAstExtractor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAGlE,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E;;;;GAIG;AACH,SAAS,eAAe,CAAC,QAAgB;IACvC,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,4BAA4B,CAAC,CAAC;IAC3D,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACjC,CAAC;AAED;;;;GAIG;AACH,SAAS,eAAe,CAAC,aAAsB,EAAE,UAAkB;IACjE,MAAM,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7E,4EAA4E;IAC5E,OAAO,GAAG,CAAC,OAAO,CAAC,mBAAmB,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;AACrD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC;IAC5B,SAAS;IACT,iBAAiB;IACjB,oBAAoB;IACpB,qBAAqB;CACtB,CAAC,CAAC;AAEH;;GAEG;AACH,SAAS,iBAAiB,CACxB,IAAa,EACb,OAAwB,EACxB,UAAkB;IAElB,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,IAAI,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACtC,OAAO,OAAO,EAAE,CAAC;QACf,IAAI,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YACpC,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YAClE,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACnC,MAAM,UAAU,GAAG,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YAClF,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QAC9B,CAAC;QACD,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACxD,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,SAAS,sBAAsB,CAC7B,SAAkB,EAClB,UAAkB,EAClB,QAAgB,EAChB,cAA6B,EAC7B,QAAgB,EAChB,YAAqC;IAErC,KAAK,MAAM,KAAK,IAAI,SAAS,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC;QAC7C,IAAI,SAAS,GAAkB,IAAI,CAAC;QACpC,IAAI,IAAY,CAAC;QAEjB,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC3B,4DAA4D;YAC5D,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YACxE,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;YAC/C,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;YAC3C,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC;QACnC,CAAC;aAAM,IAAI,KAAK,CAAC,IAAI,KAAK,mBAAmB,EAAE,CAAC;YAC9C,yEAAyE;YACzE,2EAA2E;YAC3E,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YACzE,SAAS,GAAG,QAAQ,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;YACrE,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC;QACnC,CAAC;aAAM,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YACpC,wEAAwE;YACxE,MAAM,SAAS,GAAG,KAAK,CAAC,QAAQ,EAAE,IAAI,CACpC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,YAAY,CAC7B,CAAC;YACF,IAAI,CAAC,SAAS;gBAAE,SAAS;YACzB,MAAM,MAAM,GAAG,UAAU;iBACtB,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;iBAC7C,IAAI,EAAE,CAAC;YACV,IAAI,MAAM,KAAK,QAAQ;gBAAE,SAAS;YAClC,mDAAmD;YACnD,MAAM,UAAU,GAAG,KAAK,CAAC,QAAQ,EAAE,IAAI,CACrC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,eAAe,CAChC,CAAC;YACF,SAAS,GAAG,UAAU;gBACpB,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE;gBACnE,CAAC,CAAC,IAAI,CAAC;YACT,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC;QACnC,CAAC;aAAM,CAAC;YACN,SAAS;QACX,CAAC;QAED,IAAI,SAAS,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YAClE,YAAY,CAAC,IAAI,CAAC;gBAChB,QAAQ,EAAE,OAAO;gBACjB,QAAQ,EAAE,SAAS;gBACnB,eAAe,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE;gBACtD,SAAS,EAAE,KAAK;gBAChB,QAAQ;gBACR,IAAI;gBACJ,OAAO,EAAE,QAAQ;gBACjB,cAAc;gBACd,QAAQ,EAAE,IAAI;aACf,CAAC,CAAC;QACL,CAAC;IACH,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,+BAA+B;AAC/B,8EAA8E;AAE9E;;;;GAIG;AAEH;;GAEG;AACH,SAAS,cAAc,CAAC,IAAoB,EAAE,IAAY;IACxD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC,EAAE,EAAE,CAAC;QAC9C,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QACjC,IAAI,KAAK,EAAE,IAAI,KAAK,IAAI;YAAE,OAAO,KAAK,CAAC;IACzC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;GAMG;AACH,SAAS,mBAAmB,CAAC,OAAuB;IAClD,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC;IAC1D,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAE3B,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IACtC,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAE3B,8EAA8E;IAC9E,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAC9B,qDAAqD;IACrD,yEAAyE;IACzE,yEAAyE;IACzE,4DAA4D;IAC5D,uEAAuE;IACvE,MAAM,aAAa,GAAG,QAAQ,CAAC,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC;IAC7D,MAAM,SAAS,GAAG,QAAQ,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IAChD,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACxC,MAAM,QAAQ,GAAG,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAElG,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;AAChC,CAAC;AAED,8EAA8E;AAC9E,yBAAyB;AACzB,8EAA8E;AAE9E;;;GAGG;AACH,SAAS,UAAU,CAAC,GAAQ,EAAE,IAAa;IACzC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;AAC9E,CAAC;AAED;;;GAGG;AACH,SAAS,aAAa,CAAC,GAAmB;IACxC,OAAO;QACL,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,KAAK,EAAE,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,QAAQ,CAAC;QACrC,QAAQ,EAAE;YACR,KAAK,EAAE;gBACL,IAAI,EAAE,GAAG,CAAC,aAAa,CAAC,GAAG,GAAG,CAAC;gBAC/B,MAAM,EAAE,GAAG,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC;aACrC;YACD,GAAG,EAAE;gBACH,IAAI,EAAE,GAAG,CAAC,WAAW,CAAC,GAAG,GAAG,CAAC;gBAC7B,MAAM,EAAE,GAAG,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC;aACnC;SACF;QACD,QAAQ,EAAE,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;QACxD,GAAG;KACJ,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,6BAA6B,CAC3C,GAAQ,EACR,OAAwB,EACxB,QAAgB,EAChB,UAAkB;IAElB,MAAM,YAAY,GAA4B,EAAE,CAAC;IAEjD,qBAAqB;IACrB,MAAM,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;IAE9D,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,OAAO,GAAG,OAAO,CAAC,GAAqB,CAAC;QAE9C,uEAAuE;QACvE,MAAM,YAAY,GAAG,cAAc,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;QAC1D,IAAI,CAAC,YAAY;YAAE,SAAS;QAE5B,MAAM,YAAY,GAAG,aAAa,CAAC,YAAY,CAAC,CAAC;QACjD,MAAM,QAAQ,GAAG,eAAe,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;QAC3D,IAAI,CAAC,QAAQ;YAAE,SAAS;QAExB,6CAA6C;QAC7C,MAAM,cAAc,GAAG,iBAAiB,CAAC,OAAO,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;QAEvE,yCAAyC;QACzC,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QACxC,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC,CAAC;QAErE,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;YAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAqB,CAAC;YACvC,MAAM,EAAE,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;YACpC,IAAI,CAAC,EAAE;gBAAE,SAAS;YAElB,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC;YAClC,IAAI,CAAC,QAAQ,IAAI,CAAC,QAAQ;gBAAE,SAAS;YAErC,MAAM,eAAe,GAAG,cAAc,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAC3D,MAAM,QAAQ,GAAG,eAAe,CAAC,QAAQ,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC;YACtE,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC;YAEtC,+DAA+D;YAC/D,MAAM,QAAQ,GAAG,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC;gBACxC,CAAC,CAAC,QAAQ;gBACV,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAE5E,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;gBAC3B,YAAY,CAAC,IAAI,CAAC;oBAChB,QAAQ,EAAE,GAAG,CAAC,QAAQ;oBACtB,QAAQ,EAAE,GAAG,CAAC,QAAQ;oBACtB,eAAe,EAAE,GAAG,CAAC,eAAe;oBACpC,SAAS,EAAE,KAAK;oBAChB,QAAQ;oBACR,IAAI;oBACJ,OAAO,EAAE,QAAQ;oBACjB,cAAc;oBACd,QAAQ;iBACT,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,gDAAgD;QAChD,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAClD,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,QAAQ,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;YACzC,sBAAsB,CAAC,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,cAAc,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC;QACjG,CAAC;IACH,CAAC;IAED,2EAA2E;IAC3E,8CAA8C;IAC9C,MAAM,aAAa,GAAG,OAAO,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,qBAAqB,EAAE,CAAC,CAAC;IAC9E,KAAK,MAAM,EAAE,IAAI,aAAa,EAAE,CAAC;QAC/B,MAAM,KAAK,GAAG,EAAE,CAAC,GAAqB,CAAC;QACvC,MAAM,MAAM,GAAG,cAAc,CAAC,KAAK,EAAE,gBAAgB,CAAC,EAAE,IAAI,IAAI,SAAS,CAAC;QAC1E,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACrC,MAAM,WAAW,GAAG,OAAO,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC,CAAC;QAEzE,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;YAC/B,wDAAwD;YACxD,IAAI,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YACrC,IAAI,UAAU,GAAG,WAAW,CAAC;YAC7B,OAAO,MAAM,IAAI,MAAM,CAAC,IAAI,KAAK,qBAAqB,EAAE,CAAC;gBACvD,IAAI,MAAM,CAAC,IAAI,KAAK,gBAAgB,EAAE,CAAC;oBACrC,MAAM,QAAQ,GAAI,MAAM,CAAC,GAAsB,CAAC,aAAa,CAAC,IAAI,CAChE,CAAC,CAAM,EAAE,EAAE,CACT,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI,IAAI,CAAC,CAAC,IAAI,KAAK,eAAe,CACrE,CAAC;oBACF,UAAU,GAAG,QAAQ,EAAE,IAAI,IAAI,WAAW,CAAC;oBAC3C,MAAM;gBACR,CAAC;gBACD,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;YACrC,CAAC;YAED,MAAM,GAAG,GAAG,IAAI,CAAC,GAAqB,CAAC;YACvC,MAAM,EAAE,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;YACpC,IAAI,CAAC,EAAE;gBAAE,SAAS;YAClB,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC;YAClC,IAAI,CAAC,QAAQ,IAAI,CAAC,QAAQ;gBAAE,SAAS;YAErC,MAAM,eAAe,GAAG,cAAc,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAC3D,MAAM,QAAQ,GAAG,eAAe,CAAC,QAAQ,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC;YACtE,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC;YAEtC,MAAM,QAAQ,GAAG,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC;gBACxC,CAAC,CAAC,QAAQ;gBACV,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAE5E,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;gBAC3B,YAAY,CAAC,IAAI,CAAC;oBAChB,QAAQ,EAAE,GAAG,CAAC,QAAQ;oBACtB,QAAQ,EAAE,GAAG,CAAC,QAAQ;oBACtB,eAAe,EAAE,GAAG,CAAC,eAAe;oBACpC,SAAS,EAAE,KAAK;oBAChB,QAAQ;oBACR,IAAI;oBACJ,OAAO,EAAE,UAAU;oBACnB,cAAc,EAAE,cAAc,MAAM,EAAE;oBACtC,QAAQ;iBACT,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,sFAAsF;IACtF,MAAM,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC,CAAC;IACjE,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC5B,2FAA2F;QAC3F,MAAM,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,QAAQ,GAAG,MAAM,CAAC;QACtB,IAAI,UAAU,GAAG,KAAK,CAAC;QACvB,OAAO,QAAQ,EAAE,CAAC;YAChB,IAAI,QAAQ,CAAC,IAAI,KAAK,gBAAgB,EAAE,CAAC;gBAAC,UAAU,GAAG,IAAI,CAAC;gBAAC,MAAM;YAAC,CAAC;YACrE,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACzC,CAAC;QACD,IAAI,MAAM,EAAE,IAAI,KAAK,UAAU,IAAI,MAAM,EAAE,IAAI,KAAK,OAAO,IAAI,UAAU;YAAE,SAAS;QAEpF,MAAM,GAAG,GAAG,IAAI,CAAC,GAAqB,CAAC;QACvC,MAAM,EAAE,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;QACpC,IAAI,CAAC,EAAE;YAAE,SAAS;QAElB,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC;QAClC,IAAI,CAAC,QAAQ,IAAI,CAAC,QAAQ;YAAE,SAAS;QAErC,MAAM,eAAe,GAAG,cAAc,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAC3D,MAAM,QAAQ,GAAG,eAAe,CAAC,QAAQ,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC;QACtE,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC;QAEtC,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;YAC3B,YAAY,CAAC,IAAI,CAAC;gBAChB,QAAQ,EAAE,GAAG,CAAC,QAAQ;gBACtB,QAAQ,EAAE,GAAG,CAAC,QAAQ;gBACtB,eAAe,EAAE,GAAG,CAAC,eAAe;gBACpC,SAAS,EAAE,KAAK;gBAChB,QAAQ;gBACR,IAAI;gBACJ,OAAO,EAAE,aAAa;gBACtB,cAAc,EAAE,IAAI;gBACpB,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC;oBACjC,CAAC,CAAC,QAAQ;oBACV,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;aAC5E,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,YAAY,CAAC;AACtB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CACrC,GAAQ,EACR,OAAwB,EACxB,QAAgB;IAEhB,MAAM,MAAM,GAAiB,EAAE,CAAC;IAEhC,MAAM,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC,CAAC;IACjE,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC5B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAqB,CAAC;QACvC,MAAM,EAAE,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;QACpC,IAAI,CAAC,EAAE;YAAE,SAAS;QAElB,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC;QAClC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,SAAS;QAEzC,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC;QAC9B,IAAI,IAAI,IAAI,KAAK,EAAE,CAAC;YAClB,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI;gBACJ,KAAK;gBACL,QAAQ;gBACR,SAAS,EAAE,qBAAqB;aACjC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,2BAA2B,CACzC,GAAQ,EACR,OAAwB,EACxB,QAAgB;IAEhB,MAAM,KAAK,GAAsB,EAAE,CAAC;IAEpC,gEAAgE;IAChE,iEAAiE;IACjE,MAAM,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,CAAC;IAElE,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,qEAAqE;QACrE,yDAAyD;QACzD,sEAAsE;QACtE,kEAAkE;QAClE,MAAM,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,KAAK,gBAAgB;YAAE,SAAS;QAE1D,MAAM,GAAG,GAAG,IAAI,CAAC,GAAqB,CAAC;QACvC,MAAM,SAAS,GAAG,GAAG,CAAC,IAAI,CAAC;QAC3B,IAAI,SAAS,EAAE,CAAC;YACd,KAAK,CAAC,IAAI,CAAC;gBACT,SAAS;gBACT,QAAQ;gBACR,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI;gBAC9B,SAAS,EAAE,OAAO;gBAClB,YAAY,EAAE,KAAK;aACpB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Style declaration extractor — Spec 10.
|
|
3
3
|
*
|
|
4
|
-
* Extracts normalized declarations from
|
|
5
|
-
* 1.
|
|
4
|
+
* Extracts normalized declarations from style mechanisms:
|
|
5
|
+
* 1. SCSS files — regex-based rule set extraction (CSS handled by cssAstExtractor.ts)
|
|
6
6
|
* 2. Tailwind — className/class attributes in JSX/HTML
|
|
7
7
|
* 3. Inline styles — style={{...}} object expressions
|
|
8
8
|
* 4. CSS-in-JS — styled-components / emotion tagged templates
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"styleExtractor.d.ts","sourceRoot":"","sources":["../../src/styles/styleExtractor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,eAAe,EAAW,MAAM,uBAAuB,CAAC;AAC3E,OAAO,KAAK,EAAE,qBAAqB,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAIpF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAMrE;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,eAAe,EACxB,UAAU,EAAE,MAAM,EAClB,GAAG,CAAC,EAAE,GAAG,EACT,cAAc,CAAC,EAAE,mBAAmB,GACnC,qBAAqB,EAAE,
|
|
1
|
+
{"version":3,"file":"styleExtractor.d.ts","sourceRoot":"","sources":["../../src/styles/styleExtractor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,eAAe,EAAW,MAAM,uBAAuB,CAAC;AAC3E,OAAO,KAAK,EAAE,qBAAqB,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAIpF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAMrE;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,eAAe,EACxB,UAAU,EAAE,MAAM,EAClB,GAAG,CAAC,EAAE,GAAG,EACT,cAAc,CAAC,EAAE,mBAAmB,GACnC,qBAAqB,EAAE,CAsBzB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,UAAU,EAAE,CAwBd;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,MAAM,EACnB,QAAQ,CAAC,EAAE,mBAAmB,GAC7B,mBAAmB,CAGrB;AA0ND,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,cAAc,EACzB,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,GAAG,IAAI,EAC7B,YAAY,EAAE,qBAAqB,EAAE,EACrC,QAAQ,EAAE,MAAM,GACf,IAAI,CAuFN"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Style declaration extractor — Spec 10.
|
|
3
3
|
*
|
|
4
|
-
* Extracts normalized declarations from
|
|
5
|
-
* 1.
|
|
4
|
+
* Extracts normalized declarations from style mechanisms:
|
|
5
|
+
* 1. SCSS files — regex-based rule set extraction (CSS handled by cssAstExtractor.ts)
|
|
6
6
|
* 2. Tailwind — className/class attributes in JSX/HTML
|
|
7
7
|
* 3. Inline styles — style={{...}} object expressions
|
|
8
8
|
* 4. CSS-in-JS — styled-components / emotion tagged templates
|
|
@@ -31,10 +31,11 @@ import { loadTailwindConfig } from './tailwindConfigLoader.js';
|
|
|
31
31
|
export function extractDeclarations(filePath, adapter, sourceCode, ast, tailwindTokens) {
|
|
32
32
|
const ext = filePath.includes('.') ? filePath.slice(filePath.lastIndexOf('.')) : '';
|
|
33
33
|
switch (ext) {
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
// .css files are handled by the AST pipeline (cssAstExtractor.ts +
|
|
35
|
+
// createStylesCssVisitor in pipelineAdapters.ts). SCSS stays on the
|
|
36
|
+
// regex path because tree-sitter-css is not an SCSS grammar.
|
|
36
37
|
case '.scss':
|
|
37
|
-
return
|
|
38
|
+
return extractRuleSetsFromSCSS(filePath, sourceCode);
|
|
38
39
|
case '.tsx':
|
|
39
40
|
case '.jsx':
|
|
40
41
|
case '.ts':
|
|
@@ -54,8 +55,10 @@ export function extractDeclarations(filePath, adapter, sourceCode, ast, tailwind
|
|
|
54
55
|
* and produce design tokens.
|
|
55
56
|
*/
|
|
56
57
|
export function extractTokens(filePath, sourceCode) {
|
|
58
|
+
// .css tokens are extracted via the AST pipeline (extractTokensFromCSSAst).
|
|
59
|
+
// SCSS stays on the regex path since tree-sitter-css is not an SCSS grammar.
|
|
57
60
|
const ext = filePath.includes('.') ? filePath.slice(filePath.lastIndexOf('.')) : '';
|
|
58
|
-
if (ext !== '.
|
|
61
|
+
if (ext !== '.scss')
|
|
59
62
|
return [];
|
|
60
63
|
const tokens = [];
|
|
61
64
|
const varRegex = /--([a-zA-Z0-9_-]+)\s*:\s*([^;};]+)/g;
|
|
@@ -85,12 +88,9 @@ export function getOrLoadTailwindTokens(projectRoot, existing) {
|
|
|
85
88
|
// ---------------------------------------------------------------------------
|
|
86
89
|
// Sub-extractor: CSS / SCSS
|
|
87
90
|
// ---------------------------------------------------------------------------
|
|
88
|
-
function
|
|
91
|
+
function extractRuleSetsFromSCSS(filePath, sourceCode) {
|
|
89
92
|
const declarations = [];
|
|
90
|
-
|
|
91
|
-
// We use regex for extraction because the CSS adapter's AST walk is heavy
|
|
92
|
-
// and we only need declaration extraction, not structural analysis.
|
|
93
|
-
extractRuleSets(sourceCode, filePath, mechanism, declarations);
|
|
93
|
+
extractRuleSets(sourceCode, filePath, declarations);
|
|
94
94
|
return declarations;
|
|
95
95
|
}
|
|
96
96
|
/**
|
|
@@ -100,14 +100,20 @@ function extractFromCSS(filePath, sourceCode, mechanism) {
|
|
|
100
100
|
* parsing for declaration extraction. The tree-sitter adapter handles structural
|
|
101
101
|
* analysis when needed.
|
|
102
102
|
*/
|
|
103
|
-
function extractRuleSets(css, filePath,
|
|
103
|
+
function extractRuleSets(css, filePath, declarations) {
|
|
104
104
|
// Track current context (selector, at-rule)
|
|
105
105
|
const lines = css.split('\n');
|
|
106
106
|
// Regex approach: find rule blocks with their selectors
|
|
107
107
|
let i = 0;
|
|
108
108
|
let atRuleStack = [];
|
|
109
109
|
while (i < css.length) {
|
|
110
|
-
// Skip whitespace
|
|
110
|
+
// Skip whitespace
|
|
111
|
+
while (i < css.length && (css[i] === ' ' || css[i] === '\t' || css[i] === '\n' || css[i] === '\r')) {
|
|
112
|
+
i++;
|
|
113
|
+
}
|
|
114
|
+
if (i >= css.length)
|
|
115
|
+
break;
|
|
116
|
+
// Skip comments
|
|
111
117
|
if (css[i] === '/' && css[i + 1] === '*') {
|
|
112
118
|
const end = css.indexOf('*/', i + 2);
|
|
113
119
|
if (end === -1)
|
|
@@ -143,13 +149,27 @@ function extractRuleSets(css, filePath, mechanism, declarations) {
|
|
|
143
149
|
continue;
|
|
144
150
|
}
|
|
145
151
|
}
|
|
152
|
+
// Handle closing brace of enclosing @-rule before the next opening brace.
|
|
153
|
+
// Without this, the parser never knows when to exit an @-rule block and
|
|
154
|
+
// stays inside it forever, corrupting all subsequent selector extraction.
|
|
155
|
+
const nextClose = css.indexOf('}', i);
|
|
156
|
+
if (nextClose !== -1) {
|
|
157
|
+
const nextOpen = css.indexOf('{', i);
|
|
158
|
+
if (nextOpen === -1 || nextClose < nextOpen) {
|
|
159
|
+
if (atRuleStack.length > 0) {
|
|
160
|
+
atRuleStack.pop();
|
|
161
|
+
}
|
|
162
|
+
i = nextClose + 1;
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
146
166
|
// Find the start of a rule set: selector { ... }
|
|
147
167
|
const braceOpen = css.indexOf('{', i);
|
|
148
168
|
if (braceOpen === -1)
|
|
149
169
|
break;
|
|
150
170
|
// Extract the selector (everything between last } and {)
|
|
151
171
|
const selectorStart = findSelectorStart(css, braceOpen);
|
|
152
|
-
const selector = css.slice(selectorStart, braceOpen).trim();
|
|
172
|
+
const selector = stripAllBlockComments(css.slice(selectorStart, braceOpen)).trim();
|
|
153
173
|
if (!selector || selector === '}' || selector.startsWith('@')) {
|
|
154
174
|
i = braceOpen + 1;
|
|
155
175
|
continue;
|
|
@@ -162,7 +182,7 @@ function extractRuleSets(css, filePath, mechanism, declarations) {
|
|
|
162
182
|
}
|
|
163
183
|
// Extract declarations inside this block
|
|
164
184
|
const block = css.slice(braceOpen + 1, braceClose);
|
|
165
|
-
extractDeclarationsFromBlock(block, filePath,
|
|
185
|
+
extractDeclarationsFromBlock(block, filePath, 'scss', selector, atRuleStack.length > 0 ? atRuleStack.join(', ') : null, declarations, css.slice(0, braceOpen).split('\n').length);
|
|
166
186
|
i = braceClose + 1;
|
|
167
187
|
}
|
|
168
188
|
}
|
|
@@ -179,6 +199,16 @@ function findSelectorStart(css, braceOpen) {
|
|
|
179
199
|
let depth = 0;
|
|
180
200
|
while (i >= 0) {
|
|
181
201
|
if (css[i] === '}') {
|
|
202
|
+
// When we find a } at depth 0, it's the closing brace of the
|
|
203
|
+
// preceding rule block — the selector starts right after it.
|
|
204
|
+
if (depth === 0) {
|
|
205
|
+
// Skip whitespace after the }
|
|
206
|
+
let j = i + 1;
|
|
207
|
+
while (j < css.length && (css[j] === ' ' || css[j] === '\t' || css[j] === '\n' || css[j] === '\r')) {
|
|
208
|
+
j++;
|
|
209
|
+
}
|
|
210
|
+
return j;
|
|
211
|
+
}
|
|
182
212
|
depth++;
|
|
183
213
|
i--;
|
|
184
214
|
}
|
|
@@ -270,8 +300,31 @@ export function extractDeclarationsFromBlock(block, filePath, mechanism, selecto
|
|
|
270
300
|
// Accumulate the line into the buffer
|
|
271
301
|
if (trimmed)
|
|
272
302
|
buffer += (buffer ? ' ' : '') + trimmed;
|
|
303
|
+
// Handle @apply directives — collect as synthetic declarations so CSS
|
|
304
|
+
// selector context is available for class-name discovery. @apply uses no
|
|
305
|
+
// colon, so it would be silently skipped by the colon-based branch below.
|
|
306
|
+
if (buffer.startsWith('@apply ')) {
|
|
307
|
+
const semiIdx = buffer.indexOf(';');
|
|
308
|
+
if (semiIdx !== -1) {
|
|
309
|
+
const applyValue = buffer.slice(7, semiIdx).trim();
|
|
310
|
+
buffer = buffer.slice(semiIdx + 1).trim();
|
|
311
|
+
if (applyValue && !applyValue.includes('{')) {
|
|
312
|
+
declarations.push({
|
|
313
|
+
property: 'apply',
|
|
314
|
+
rawValue: applyValue,
|
|
315
|
+
normalizedValue: { type: 'literal', value: applyValue },
|
|
316
|
+
mechanism,
|
|
317
|
+
filePath,
|
|
318
|
+
line: baseLine + lineInBlock,
|
|
319
|
+
context: selector,
|
|
320
|
+
variantContext,
|
|
321
|
+
tokenRef: null,
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
273
326
|
// Check if this buffer contains a complete declaration
|
|
274
|
-
if (buffer.includes(':')) {
|
|
327
|
+
else if (buffer.includes(':')) {
|
|
275
328
|
const semiIdx = buffer.indexOf(';');
|
|
276
329
|
if (semiIdx !== -1) {
|
|
277
330
|
const decl = buffer.slice(0, semiIdx).trim();
|