( selector: string )
| 1009 | * - Pure element/id selectors |
| 1010 | */ |
| 1011 | export const parseClassBasedSelector = ( |
| 1012 | selector: string |
| 1013 | ): ParsedClassSelector | undefined => { |
| 1014 | let ast: csstree.CssNode; |
| 1015 | try { |
| 1016 | ast = csstree.parse(selector, { context: "selector" }); |
| 1017 | } catch { |
| 1018 | return undefined; |
| 1019 | } |
| 1020 | |
| 1021 | if (ast.type !== "Selector") { |
| 1022 | return undefined; |
| 1023 | } |
| 1024 | |
| 1025 | const children = ast.children.toArray(); |
| 1026 | if (children.length === 0) { |
| 1027 | return undefined; |
| 1028 | } |
| 1029 | |
| 1030 | // First node must be a ClassSelector |
| 1031 | if (children[0].type !== "ClassSelector") { |
| 1032 | return undefined; |
| 1033 | } |
| 1034 | |
| 1035 | // Split children into segments at Combinator nodes |
| 1036 | type Segment = { |
| 1037 | nodes: csstree.CssNode[]; |
| 1038 | combinator?: "descendant" | "child"; |
| 1039 | }; |
| 1040 | const segments: Segment[] = []; |
| 1041 | let currentNodes: csstree.CssNode[] = []; |
| 1042 | |
| 1043 | for (const child of children) { |
| 1044 | if (child.type === "Combinator") { |
| 1045 | if (child.name === " ") { |
| 1046 | segments.push({ nodes: currentNodes, combinator: "descendant" }); |
| 1047 | } else if (child.name === ">") { |
| 1048 | segments.push({ nodes: currentNodes, combinator: "child" }); |
| 1049 | } else { |
| 1050 | // Sibling combinators (+, ~) — reject |
| 1051 | return undefined; |
| 1052 | } |
| 1053 | currentNodes = []; |
| 1054 | } else { |
| 1055 | currentNodes.push(child); |
| 1056 | } |
| 1057 | } |
| 1058 | // Push the last (target) segment |
| 1059 | segments.push({ nodes: currentNodes }); |
| 1060 | |
| 1061 | // Validate all segments and extract class names |
| 1062 | const ancestors: Array<{ |
| 1063 | classNames: string[]; |
| 1064 | combinator: "descendant" | "child"; |
| 1065 | }> = []; |
| 1066 | |
| 1067 | // Process ancestor segments (all except the last) |
| 1068 | for (let i = 0; i < segments.length - 1; i++) { |
no test coverage detected