( node: Rule, variant: Variant, variants: Variants, depth: number = 0, )
| 175 | } |
| 176 | |
| 177 | export function applyVariant( |
| 178 | node: Rule, |
| 179 | variant: Variant, |
| 180 | variants: Variants, |
| 181 | depth: number = 0, |
| 182 | ): null | void { |
| 183 | if (variant.kind === 'arbitrary') { |
| 184 | // Relative selectors are not valid as an entire arbitrary variant, only as |
| 185 | // an arbitrary variant that is part of another compound variant. |
| 186 | // |
| 187 | // E.g. `[>img]:flex` is not valid, but `has-[>img]:flex` is |
| 188 | if (variant.relative && depth === 0) return null |
| 189 | |
| 190 | node.nodes = [rule(variant.selector, node.nodes)] |
| 191 | return |
| 192 | } |
| 193 | |
| 194 | // SAFETY: At this point it is safe to use TypeScript's non-null assertion |
| 195 | // operator because if the `candidate.root` didn't exist, `parseCandidate` |
| 196 | // would have returned `null` and we would have returned early resulting in |
| 197 | // not hitting this code path. |
| 198 | let { applyFn } = variants.get(variant.root)! |
| 199 | |
| 200 | if (variant.kind === 'compound') { |
| 201 | // Some variants traverse the AST to mutate the nodes. E.g.: `group-*` wants |
| 202 | // to prefix every selector of the variant it's compounding with `.group`. |
| 203 | // |
| 204 | // E.g.: |
| 205 | // ``` |
| 206 | // group-hover:[&_p]:flex |
| 207 | // ``` |
| 208 | // |
| 209 | // Should only prefix the `group-hover` part with `.group`, and not the `&_p` part. |
| 210 | // |
| 211 | // To solve this, we provide an isolated placeholder node to the variant. |
| 212 | // The variant can now apply its logic to the isolated node without |
| 213 | // affecting the original node. |
| 214 | let isolatedNode = atRule('@slot') |
| 215 | |
| 216 | let result = applyVariant(isolatedNode, variant.variant, variants, depth + 1) |
| 217 | if (result === null) return null |
| 218 | |
| 219 | if (variant.root === 'not' && isolatedNode.nodes.length > 1) { |
| 220 | // The `not` variant cannot negate sibling rules / at-rules because these |
| 221 | // are an OR relationship. Doing so would require transforming sibling |
| 222 | // nodes into nesting while negating them. This isn't possible with the |
| 223 | // current implementation of the `not` variant or with how variants are |
| 224 | // applied in general (on a per-node basis). |
| 225 | return null |
| 226 | } |
| 227 | |
| 228 | for (let child of isolatedNode.nodes) { |
| 229 | // Only some variants wrap children in rules. For example, the `force` |
| 230 | // variant is a noop on the AST. And the `has` variant modifies the |
| 231 | // selector rather than the children. |
| 232 | // |
| 233 | // This means `child` may be a declaration and we don't want to apply the |
| 234 | // variant to it. This also means the entire variant as a whole is not |
no test coverage detected