|
1 | | -import walk from 'css-tree/walker' |
2 | | -import parse from 'css-tree/parser' |
3 | | -import type { CssNode, CssLocation as CssTreeLocation } from 'css-tree' |
| 1 | +import { parse, walk, DECLARATION, FUNCTION, AT_RULE, IDENTIFIER } from '@projectwallace/css-parser' |
| 2 | +import type { CSSNode } from '@projectwallace/css-parser' |
4 | 3 | import type { CssLocation } from '$lib/css-location' |
5 | 4 |
|
6 | | -function to_loc(loc: CssTreeLocation): CssLocation { |
| 5 | +function to_loc(node: CSSNode): CssLocation { |
7 | 6 | return { |
8 | | - line: loc.start.line, |
9 | | - column: loc.start.column, |
10 | | - offset: loc.start.offset, |
11 | | - length: loc.end.offset - loc.start.offset |
| 7 | + line: node.line, |
| 8 | + column: node.column, |
| 9 | + offset: node.start, |
| 10 | + length: node.length |
12 | 11 | } |
13 | 12 | } |
14 | 13 |
|
15 | 14 | export function analyze(css: string) { |
16 | | - let ast = parse(css, { |
17 | | - positions: true, |
18 | | - parseCustomProperty: true, |
19 | | - parseRulePrelude: false, |
20 | | - }) |
| 15 | + let ast = parse(css) |
21 | 16 | let declared_properties = new Set<string>() |
22 | 17 | let used_properties = new Set<string>() |
23 | 18 | let all_properties = new Map<string, CssLocation[]>() |
24 | 19 | let declared_with_fallback = new Set<string>() |
25 | 20 |
|
26 | | - walk(ast, function (node: CssNode) { |
27 | | - if (node.type === 'Declaration') { |
28 | | - if (node.property.startsWith('--')) { |
29 | | - let loc = to_loc(node.loc!) |
30 | | - let name = node.property |
31 | | - declared_properties.add(name) |
32 | | - all_properties.set(name, (all_properties.get(name) ?? []).concat(loc)) |
33 | | - } |
34 | | - } else if (node.type === 'Function' && node.name === 'var') { |
35 | | - let first_child = node.children.first |
36 | | - if ( |
37 | | - first_child !== null && |
38 | | - first_child.type === 'Identifier' && |
39 | | - first_child.name.startsWith('--') |
40 | | - ) { |
41 | | - let node_loc = first_child.loc! |
42 | | - if (this.declaration !== null) { |
43 | | - node_loc = this.declaration.loc! |
44 | | - } |
45 | | - let loc = to_loc(node_loc) |
| 21 | + // Helper to recursively walk values and find all var() functions |
| 22 | + function walk_values(value_node: CSSNode, declaration_node: CSSNode) { |
| 23 | + if (value_node.type === FUNCTION && value_node.name === 'var') { |
| 24 | + let first_child = value_node.first_child |
| 25 | + if (first_child !== null && first_child.type === IDENTIFIER && first_child.name.startsWith('--')) { |
| 26 | + let loc = to_loc(declaration_node) |
46 | 27 | let name = first_child.name |
47 | 28 | used_properties.add(name) |
48 | 29 | all_properties.set(name, (all_properties.get(name) ?? []).concat(loc)) |
49 | 30 |
|
50 | 31 | // check if it has a fallback value that is a custom property |
51 | | - let second_child = node.children.toArray()[1] |
52 | | - if (second_child !== undefined && !(second_child.type === 'Function' && second_child.name === 'var')) { |
| 32 | + let children = value_node.children |
| 33 | + let second_child = children[1] |
| 34 | + if (second_child !== undefined && !(second_child.type === FUNCTION && second_child.name === 'var')) { |
53 | 35 | declared_with_fallback.add(name) |
54 | 36 | } |
55 | 37 | } |
56 | | - } else if ( |
57 | | - node.type === 'Atrule' && |
58 | | - node.name === 'property' && |
59 | | - node.prelude !== null && |
60 | | - node.prelude.type === 'AtrulePrelude' |
61 | | - ) { |
62 | | - let first_child = node.prelude.children.first |
63 | | - if (first_child !== null && first_child.type === 'Identifier') { |
64 | | - let name = first_child.name |
65 | | - let loc = to_loc(node.loc!) |
| 38 | + } |
| 39 | + |
| 40 | + // Recursively walk children to find nested var() calls |
| 41 | + for (let child of value_node.children) { |
| 42 | + walk_values(child, declaration_node) |
| 43 | + } |
| 44 | + } |
| 45 | + |
| 46 | + walk(ast, (node: CSSNode) => { |
| 47 | + if (node.type === DECLARATION) { |
| 48 | + if (node.property.startsWith('--')) { |
| 49 | + let loc = to_loc(node) |
| 50 | + let name = node.property |
| 51 | + declared_properties.add(name) |
| 52 | + all_properties.set(name, (all_properties.get(name) ?? []).concat(loc)) |
| 53 | + } |
| 54 | + |
| 55 | + // Check for var() usage in declaration values (recursively) |
| 56 | + for (let value of node.values) { |
| 57 | + walk_values(value, node) |
| 58 | + } |
| 59 | + } else if (node.type === AT_RULE && node.name === 'property') { |
| 60 | + let first_child = node.first_child |
| 61 | + if (first_child !== null && first_child.type === IDENTIFIER) { |
| 62 | + let name = first_child.text // Use .text instead of .name |
| 63 | + let loc = to_loc(node) |
66 | 64 | declared_properties.add(name) |
67 | 65 | all_properties.set(name, (all_properties.get(name) ?? []).concat(loc)) |
68 | 66 | } |
|
0 commit comments