Skip to content

Commit d063a8c

Browse files
committed
rewrite parseCss to support nesting
1 parent 06872a5 commit d063a8c

File tree

2 files changed

+48
-32
lines changed

2 files changed

+48
-32
lines changed

packages/css-data/src/parse-css.test.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ describe("Parse CSS", () => {
195195
});
196196

197197
test("parse first pseudo class as selector", () => {
198+
// E.g. :root
198199
expect(parseCss(`:first-pseudo:my-state { color: #ff0000 }`)).toEqual([
199200
{
200201
selector: ":first-pseudo",
@@ -232,7 +233,7 @@ describe("Parse CSS", () => {
232233
]);
233234
});
234235

235-
test.only("parse multiple selectors, both with state", () => {
236+
test("parse multiple selectors, both with state", () => {
236237
expect(parseCss(`a:active, a:hover { color: #ff0000 }`)).toEqual([
237238
{
238239
selector: "a",

packages/css-data/src/parse-css.ts

+46-31
Original file line numberDiff line numberDiff line change
@@ -169,43 +169,58 @@ export const parseCss = (css: string, options: ParserOptions = {}) => {
169169
breakpoint = generated;
170170
}
171171
}
172-
if (invalidBreakpoint) {
172+
if (invalidBreakpoint || this.rule.prelude.type !== "SelectorList") {
173173
return;
174174
}
175175

176176
const selectors: Selector[] = [];
177-
if (this.rule.prelude.type === "SelectorList") {
178-
for (const selector of this.rule.prelude.children) {
179-
if (selector.type !== "Selector" || selector.children.size > 2) {
180-
continue;
181-
}
182-
const [nameNode, stateNode] = selector.children;
183-
let name;
184-
if (
185-
nameNode.type === "ClassSelector" ||
186-
nameNode.type === "TypeSelector"
187-
) {
188-
name = nameNode.name;
189-
} else if (nameNode.type === "PseudoClassSelector") {
190-
name = `:${nameNode.name}`;
191-
} else {
192-
continue;
177+
178+
for (const node of this.rule.prelude.children) {
179+
if (node.type !== "Selector") {
180+
continue;
181+
}
182+
let selector: Selector | undefined = undefined;
183+
node.children.forEach((node) => {
184+
let name: string = "";
185+
let state: string | undefined;
186+
switch (node.type) {
187+
case "TypeSelector":
188+
name = node.name;
189+
break;
190+
case "ClassSelector":
191+
// .a {} vs a.b {}
192+
name = selector ? `.${node.name}` : node.name;
193+
break;
194+
case "PseudoClassSelector": {
195+
// First pseudo selector is not a state but an element selector, e.g. :root
196+
if (selector) {
197+
state = `:${node.name}`;
198+
} else {
199+
name = `:${node.name}`;
200+
}
201+
break;
202+
}
203+
case "Combinator":
204+
// " " vs " > "
205+
name = node.name === " " ? node.name : ` ${node.name} `;
206+
break;
207+
case "PseudoElementSelector":
208+
state = `::${node.name}`;
209+
break;
193210
}
194-
if (stateNode?.type === "PseudoClassSelector") {
195-
selectors.push({
196-
name,
197-
state: `:${stateNode.name}`,
198-
});
199-
} else if (stateNode?.type === "PseudoElementSelector") {
200-
selectors.push({
201-
name,
202-
state: `::${stateNode.name}`,
203-
});
204-
} else {
205-
selectors.push({
206-
name,
207-
});
211+
212+
if (selector) {
213+
selector.name += name;
214+
if (state) {
215+
selector.state = state;
216+
}
217+
return;
208218
}
219+
selector = { name, state };
220+
});
221+
if (selector) {
222+
selectors.push(selector);
223+
selector = undefined;
209224
}
210225
}
211226

0 commit comments

Comments
 (0)