diff --git a/src/compiler/__tests__/compiler.test.tsx b/src/compiler/__tests__/compiler.test.tsx index c790998..ac9434d 100644 --- a/src/compiler/__tests__/compiler.test.tsx +++ b/src/compiler/__tests__/compiler.test.tsx @@ -280,3 +280,96 @@ test("light-dark()", () => { ], }); }); + +test("media query nested in rules", () => { + const compiled = compile(` +.my-class { + color: red; + @media (min-width: 600px) { + color: blue; + + @media (min-width: 400px) { + background-color: green; + } + } + + @media (min-width: 100px) { + background-color: yellow; + } +}`); + + expect(compiled).toStrictEqual({ + s: [ + [ + "my-class", + [ + { + d: [ + { + color: "#f00", + }, + ], + s: [1, 1], + v: [["__rn-css-color", "#f00"]], + }, + { + d: [ + { + color: "#00f", + }, + ], + m: [[">=", "width", 600]], + s: [2, 1], + v: [["__rn-css-color", "#00f"]], + }, + { + d: [ + { + backgroundColor: "#008000", + }, + ], + m: [[">=", "width", 400]], + s: [3, 1], + }, + { + d: [ + { + backgroundColor: "#ff0", + }, + ], + m: [[">=", "width", 100]], + s: [4, 1], + }, + { + d: [ + { + color: "#00f", + }, + ], + m: [[">=", "width", 600]], + s: [2, 1, 1], + v: [["__rn-css-color", "#00f"]], + }, + { + d: [ + { + backgroundColor: "#008000", + }, + ], + m: [[">=", "width", 400]], + s: [3, 1, 1], + }, + { + d: [ + { + backgroundColor: "#ff0", + }, + ], + m: [[">=", "width", 100]], + s: [4, 1, 1], + }, + ], + ], + ], + }); +}); diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index bf70bba..19bcb7d 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -132,25 +132,50 @@ function extractRule(rule: Rule, builder: StylesheetBuilder) { extractMedia(rule.value, builder); break; } + case "nested-declarations": { + const value = rule.value; + + const declarationBlock = value.declarations; + if (declarationBlock) { + if (declarationBlock.declarations) { + builder.newRuleFork(); + for (const declaration of declarationBlock.declarations) { + parseDeclaration(declaration, builder); + } + builder.applyRuleToSelectors(); + } + + if (declarationBlock.importantDeclarations) { + builder.newRuleFork({ important: true }); + for (const declaration of declarationBlock.importantDeclarations) { + parseDeclaration(declaration, builder); + } + builder.applyRuleToSelectors(); + } + } + break; + } case "style": { - // If the rule is a style declaration, extract it with the `getExtractedStyle` function and store it in the `declarations` map - builder = builder.fork("style"); + const value = rule.value; - const declarationBlock = rule.value.declarations; - const mapping = parsePropAtRule(rule.value.rules); + const declarationBlock = value.declarations; + const mapping = parsePropAtRule(value.rules); const selectors = getSelectors( - rule.value.selectors, + value.selectors, false, builder.getOptions(), ); + // If the rule is a style declaration, extract it with the `getExtractedStyle` function and store it in the `declarations` map + builder = builder.fork("style", selectors); + if (declarationBlock) { if (declarationBlock.declarations) { builder.newRule(mapping); for (const declaration of declarationBlock.declarations) { parseDeclaration(declaration, builder); } - builder.applyRuleToSelectors(selectors); + builder.applyRuleToSelectors(); } if (declarationBlock.importantDeclarations) { @@ -158,9 +183,16 @@ function extractRule(rule: Rule, builder: StylesheetBuilder) { for (const declaration of declarationBlock.importantDeclarations) { parseDeclaration(declaration, builder); } - builder.applyRuleToSelectors(selectors); + builder.applyRuleToSelectors(); } } + + if (value.rules) { + for (const nestedRule of value.rules) { + extractRule(nestedRule, builder); + } + } + break; } case "layer-block": @@ -184,7 +216,6 @@ function extractRule(rule: Rule, builder: StylesheetBuilder) { case "counter-style": case "moz-document": case "nesting": - case "nested-declarations": case "viewport": case "custom-media": case "scope": diff --git a/src/compiler/stylesheet.ts b/src/compiler/stylesheet.ts index d4516ac..39558f8 100644 --- a/src/compiler/stylesheet.ts +++ b/src/compiler/stylesheet.ts @@ -57,17 +57,22 @@ export class StylesheetBuilder { rem: number; ruleOrder: number; } = { ruleSets: {}, rem: 14, ruleOrder: 0 }, + private selectors?: NormalizeSelector[], ) {} - fork(mode = this.mode, rule?: Partial): StylesheetBuilder { + fork( + mode = this.mode, + selectors: NormalizeSelector[] | undefined = this.selectors, + ): StylesheetBuilder { this.shared.ruleOrder++; return new StylesheetBuilder( this.options, mode, - this.cloneRule(rule ? { ...this.rule, ...rule } : undefined), + this.cloneRule(), { ...this.mapping }, this.descriptorProperty, this.shared, + selectors, ); } @@ -166,6 +171,14 @@ export class StylesheetBuilder { } } + newRuleFork({ important = false } = {}) { + this.rule = this.cloneRule(this.rule); + this.rule.s[Specificity.Order] = this.shared.ruleOrder; + if (important) { + this.rule.s[Specificity.Important] = 1; + } + } + addExtraRule(rule: Partial) { let extraRuleArray = extraRules.get(this.rule); if (!extraRuleArray) { @@ -331,7 +344,12 @@ export class StylesheetBuilder { } } - applyRuleToSelectors(selectorList: NormalizeSelector[]): void { + applyRuleToSelectors(selectorList = this.selectors): void { + if (!selectorList?.length) { + // If there are no selectors, we cannot apply the rule + return; + } + if (!this.rule.d && !this.rule.v) { return; }