@tenphi/tasty 2.0.2 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -246,7 +246,14 @@ function expandOrConditions(entries) {
246
246
  return result;
247
247
  }
248
248
  /**
249
- * Expand a single entry's OR condition into multiple exclusive entries
249
+ * Expand a single entry's OR condition into multiple exclusive entries.
250
+ *
251
+ * Note: branches are NOT sorted by at-rule context here (unlike the
252
+ * `expandExclusiveOrs` pass below). User-authored ORs in state keys aren't
253
+ * the product of De Morgan negation, so each branch is expected to render
254
+ * independently in its own scope and at-rule sort isn't load-bearing.
255
+ * The post-build pass needs the sort because it has to preserve at-rule
256
+ * wrapping across branches that came from negating a compound at-rule.
250
257
  */
251
258
  function expandSingleEntry(entry) {
252
259
  const orBranches = collectOrBranches(entry.condition);
@@ -1 +1 @@
1
- {"version":3,"file":"exclusive.js","names":[],"sources":["../../src/pipeline/exclusive.ts"],"sourcesContent":["/**\n * Exclusive Condition Builder\n *\n * Transforms parsed style entries into exclusive conditions.\n * Each entry's condition is ANDed with the negation of all higher-priority conditions,\n * ensuring exactly one condition matches at any given time.\n */\n\nimport type { StyleValue } from '../utils/styles';\n\nimport type { ConditionNode } from './conditions';\nimport { and, isCompoundCondition, not, or, trueCondition } from './conditions';\nimport { simplifyCondition } from './simplify';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Parsed style entry with condition\n */\nexport interface ParsedStyleEntry {\n styleKey: string; // e.g., 'padding', 'fill'\n stateKey: string; // Original key: '', 'compact', '@media(w < 768px)'\n value: StyleValue; // The style value (before handler processing)\n condition: ConditionNode; // Parsed condition tree\n priority: number; // Order in original object (higher = higher priority)\n}\n\n/**\n * Style entry with exclusive condition\n */\nexport interface ExclusiveStyleEntry extends ParsedStyleEntry {\n exclusiveCondition: ConditionNode; // condition & !higherPriorityConditions\n}\n\n// ============================================================================\n// Main Function\n// ============================================================================\n\n/**\n * Build exclusive conditions for a list of parsed style entries.\n *\n * The entries should be ordered by priority (highest priority first).\n *\n * For each entry, we compute:\n * exclusiveCondition = condition & !prior[0] & !prior[1] & ...\n *\n * This ensures exactly one condition matches at any time.\n *\n * Example:\n * Input (ordered highest to lowest priority):\n * A: value1 (priority 2)\n * B: value2 (priority 1)\n * C: value3 (priority 0)\n *\n * Output:\n * A: A\n * B: B & !A\n * C: C & !A & !B\n *\n * @param entries Parsed style entries ordered by priority (highest first)\n * @returns Entries with exclusive conditions, filtered to remove impossible ones\n */\nexport function buildExclusiveConditions(\n entries: ParsedStyleEntry[],\n): ExclusiveStyleEntry[] {\n const result: ExclusiveStyleEntry[] = [];\n const priorConditions: ConditionNode[] = [];\n\n for (const entry of entries) {\n // Build: condition & !prior[0] & !prior[1] & ...\n let exclusive: ConditionNode = entry.condition;\n\n for (const prior of priorConditions) {\n // Skip negating \"always true\" (default state) - it would become \"always false\"\n if (prior.kind !== 'true') {\n exclusive = and(exclusive, not(prior));\n }\n }\n\n // Simplify the exclusive condition\n const simplified = simplifyCondition(exclusive);\n\n // Skip impossible conditions (simplified to FALSE)\n if (simplified.kind === 'false') {\n continue;\n }\n\n result.push({\n ...entry,\n exclusiveCondition: simplified,\n });\n\n // Add non-default conditions to prior list for subsequent entries\n if (entry.condition.kind !== 'true') {\n priorConditions.push(entry.condition);\n }\n }\n\n return result;\n}\n\n/**\n * Parse style entries from a value mapping object.\n *\n * @param styleKey The style key (e.g., 'padding')\n * @param valueMap The value mapping { '': '2x', 'compact': '1x', '@media(w < 768px)': '0.5x' }\n * @param parseCondition Function to parse state keys into conditions\n * @returns Parsed entries ordered by priority (highest first)\n */\nexport function parseStyleEntries(\n styleKey: string,\n valueMap: Record<string, StyleValue>,\n parseCondition: (stateKey: string) => ConditionNode,\n): ParsedStyleEntry[] {\n const entries: ParsedStyleEntry[] = [];\n const keys = Object.keys(valueMap);\n\n keys.forEach((stateKey, index) => {\n const value = valueMap[stateKey];\n const condition =\n stateKey === '' ? trueCondition() : parseCondition(stateKey);\n\n entries.push({\n styleKey,\n stateKey,\n value,\n condition,\n priority: index,\n });\n });\n\n // Reverse so highest priority (last in object) comes first for exclusive building\n // buildExclusiveConditions expects highest priority first\n entries.reverse();\n\n return entries;\n}\n\n/**\n * Merge parsed entries that share the same value.\n *\n * When multiple **non-default** state keys map to the same value, their\n * conditions can be combined with OR and treated as a single entry.\n * This must happen **before** exclusive expansion and OR branch splitting\n * to avoid combinatorial explosion and duplicate CSS output.\n *\n * Default (TRUE) entries are **never** merged with non-default entries.\n * Merging `TRUE | X` collapses to `TRUE`, destroying the non-default\n * condition's participation in exclusive building. That causes\n * intermediate-priority states to lose their `:not(X)` negation,\n * breaking mutual exclusivity when X and an intermediate state are\n * both active. Stage 6 `mergeByValue` handles combining rules with\n * identical CSS output after exclusive conditions are correctly built.\n *\n * Example: `{ '@dark': 'red', '@dark & @hc': 'red' }` merges into a\n * single entry with condition `@dark | (@dark & @hc)` = `@dark`.\n *\n * Entries are ordered highest-priority-first. The merged entry keeps the\n * highest priority of the group.\n */\nexport function mergeEntriesByValue(\n entries: ParsedStyleEntry[],\n): ParsedStyleEntry[] {\n if (entries.length <= 1) return entries;\n\n const groups = new Map<\n string,\n { entries: ParsedStyleEntry[]; maxPriority: number }\n >();\n\n for (const entry of entries) {\n const valueKey = serializeValue(entry.value);\n const group = groups.get(valueKey);\n if (group) {\n group.entries.push(entry);\n group.maxPriority = Math.max(group.maxPriority, entry.priority);\n } else {\n groups.set(valueKey, { entries: [entry], maxPriority: entry.priority });\n }\n }\n\n // If no merges possible, return as-is\n if (groups.size === entries.length) return entries;\n\n const merged: ParsedStyleEntry[] = [];\n for (const [, group] of groups) {\n if (group.entries.length === 1) {\n merged.push(group.entries[0]);\n continue;\n }\n\n // Separate default (TRUE) entries from non-default entries.\n // Default entries must stay separate so that non-default conditions\n // participate in exclusive building and block intermediate states.\n const defaultEntries = group.entries.filter(\n (e) => e.condition.kind === 'true',\n );\n const nonDefaultEntries = group.entries.filter(\n (e) => e.condition.kind !== 'true',\n );\n\n // Keep default entries as-is\n for (const entry of defaultEntries) {\n merged.push(entry);\n }\n\n // Merge only non-default entries\n if (nonDefaultEntries.length === 1) {\n merged.push(nonDefaultEntries[0]);\n } else if (nonDefaultEntries.length >= 2) {\n const combinedCondition = simplifyCondition(\n or(...nonDefaultEntries.map((e) => e.condition)),\n );\n\n const combinedStateKey = nonDefaultEntries\n .map((e) => e.stateKey)\n .join(' | ');\n\n merged.push({\n styleKey: nonDefaultEntries[0].styleKey,\n stateKey: combinedStateKey,\n value: nonDefaultEntries[0].value,\n condition: combinedCondition,\n priority: group.maxPriority,\n });\n }\n }\n\n // Re-sort by priority (highest first)\n merged.sort((a, b) => b.priority - a.priority);\n\n return merged;\n}\n\nfunction serializeValue(value: StyleValue): string {\n if (value === null || value === undefined) return 'null';\n if (typeof value === 'string' || typeof value === 'number') {\n return String(value);\n }\n return JSON.stringify(value);\n}\n\n// ============================================================================\n// Compound State Extraction\n// ============================================================================\n\n/**\n * Eliminate redundant state dimensions from a value map.\n *\n * When a value map contains compound AND state keys (e.g. `@dark & @hc`),\n * checks whether any state atom is a \"don't-care\" variable — i.e. the\n * value is the same whether that atom is present or absent. Redundant\n * atoms are removed from all keys and duplicate entries are collapsed.\n *\n * This runs **before** condition parsing so that downstream stages\n * (`mergeEntriesByValue`, `buildExclusiveConditions`, materialization)\n * never see the irrelevant dimension, producing simpler, smaller CSS.\n *\n * Only pure top-level AND combinations are eligible. Keys that contain\n * `|`, `^`, or `,` at the top level are treated as opaque single atoms.\n *\n * @example\n * { '': A, '@dark': B, '@hc': A, '@dark & @hc': B }\n * // @hc is redundant → { '': A, '@dark': B }\n */\nexport function extractCompoundStates(\n valueMap: Record<string, StyleValue>,\n): Record<string, StyleValue> {\n const keys = Object.keys(valueMap);\n\n if (keys.length < 3 || !keys.some((k) => k.includes('&'))) {\n return valueMap;\n }\n\n const entries = keys.map((key) => {\n const atoms = splitTopLevelAnd(key);\n return {\n // null means the key has non-AND operators; treat the whole key\n // as a single opaque atom so it never matches partial pairs.\n atoms: atoms ?? [key],\n value: valueMap[key],\n };\n });\n\n const allAtoms = new Set<string>();\n for (const e of entries) {\n for (const a of e.atoms) allAtoms.add(a);\n }\n\n const redundant = new Set<string>();\n for (const atom of allAtoms) {\n if (isAtomRedundant(entries, atom)) {\n redundant.add(atom);\n }\n }\n\n if (redundant.size === 0) return valueMap;\n\n const newMap: Record<string, StyleValue> = {};\n for (const e of entries) {\n const filtered = e.atoms.filter((a) => !redundant.has(a));\n const newKey = filtered.join(' & ');\n if (!(newKey in newMap)) {\n newMap[newKey] = e.value;\n }\n }\n\n return newMap;\n}\n\n/**\n * Split a state key by top-level `&` operators.\n *\n * Returns `null` if the key contains `|`, `^`, or `,` at the top level\n * (making it ineligible for atom-level extraction).\n * Returns `[]` for the empty string (default key).\n */\nfunction splitTopLevelAnd(key: string): string[] | null {\n if (key === '') return [];\n\n const parts: string[] = [];\n let depth = 0;\n let current = '';\n\n for (const ch of key) {\n if (ch === '(' || ch === '[') depth++;\n else if (ch === ')' || ch === ']') depth--;\n\n if (depth === 0) {\n if (ch === '&') {\n const trimmed = current.trim();\n if (trimmed) parts.push(trimmed);\n current = '';\n continue;\n }\n if (ch === '|' || ch === '^' || ch === ',') {\n return null;\n }\n }\n\n current += ch;\n }\n\n const trimmed = current.trim();\n if (trimmed) parts.push(trimmed);\n\n return parts;\n}\n\n/**\n * An atom is redundant when every entry that contains it has a matching\n * partner (same remaining atoms, atom absent) with the same value.\n */\nfunction isAtomRedundant(\n entries: { atoms: string[]; value: StyleValue }[],\n atom: string,\n): boolean {\n const withAtom = entries.filter((e) => e.atoms.includes(atom));\n if (withAtom.length === 0) return false;\n\n for (const wa of withAtom) {\n const remaining = wa.atoms.filter((a) => a !== atom);\n\n const pair = entries.find(\n (e) =>\n !e.atoms.includes(atom) &&\n e.atoms.length === remaining.length &&\n remaining.every((r) => e.atoms.includes(r)),\n );\n\n if (!pair) return false;\n if (serializeValue(wa.value) !== serializeValue(pair.value)) return false;\n }\n\n return true;\n}\n\n/**\n * Check if a value is a style value mapping (object with state keys)\n */\nexport function isValueMapping(\n value: StyleValue | Record<string, StyleValue>,\n): value is Record<string, StyleValue> {\n return (\n value !== null &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n !(value instanceof Date)\n );\n}\n\n// ============================================================================\n// OR Expansion\n// ============================================================================\n\n/**\n * Expand OR conditions in parsed entries into multiple exclusive entries.\n *\n * For an entry with condition `A | B | C`, this creates 3 entries:\n * - condition: A\n * - condition: B & !A\n * - condition: C & !A & !B\n *\n * This ensures OR branches are mutually exclusive BEFORE the main\n * exclusive condition building pass.\n *\n * @param entries Parsed entries (may contain OR conditions)\n * @returns Expanded entries with OR branches made exclusive\n */\nexport function expandOrConditions(\n entries: ParsedStyleEntry[],\n): ParsedStyleEntry[] {\n const result: ParsedStyleEntry[] = [];\n\n for (const entry of entries) {\n const expanded = expandSingleEntry(entry);\n result.push(...expanded);\n }\n\n return result;\n}\n\n/**\n * Expand a single entry's OR condition into multiple exclusive entries\n */\nfunction expandSingleEntry(entry: ParsedStyleEntry): ParsedStyleEntry[] {\n const orBranches = collectOrBranches(entry.condition);\n\n // If no OR (single branch), return as-is\n if (orBranches.length <= 1) {\n return [entry];\n }\n\n // Make each OR branch exclusive from prior branches\n const result: ParsedStyleEntry[] = [];\n const priorBranches: ConditionNode[] = [];\n\n for (let i = 0; i < orBranches.length; i++) {\n const branch = orBranches[i];\n\n // Build: branch & !prior[0] & !prior[1] & ...\n let exclusiveBranch: ConditionNode = branch;\n for (const prior of priorBranches) {\n exclusiveBranch = and(exclusiveBranch, not(prior));\n }\n\n // Simplify to detect impossible combinations\n const simplified = simplifyCondition(exclusiveBranch);\n\n // Skip impossible branches\n if (simplified.kind === 'false') {\n priorBranches.push(branch);\n continue;\n }\n\n result.push({\n ...entry,\n stateKey: `${entry.stateKey}[${i}]`, // Mark as expanded branch\n condition: simplified,\n // Keep same priority - all branches from same entry have same priority\n });\n\n priorBranches.push(branch);\n }\n\n return result;\n}\n\n/**\n * Collect top-level OR branches from a condition.\n *\n * For `A | B | C`, returns [A, B, C]\n * For `A & B`, returns [A & B] (single branch)\n * For `A | (B & C)`, returns [A, B & C]\n */\nfunction collectOrBranches(condition: ConditionNode): ConditionNode[] {\n if (condition.kind === 'true' || condition.kind === 'false') {\n return [condition];\n }\n\n if (isCompoundCondition(condition) && condition.operator === 'OR') {\n // Flatten nested ORs\n const branches: ConditionNode[] = [];\n for (const child of condition.children) {\n branches.push(...collectOrBranches(child));\n }\n return branches;\n }\n\n // Not an OR - return as single branch\n return [condition];\n}\n\n// ============================================================================\n// Post-Build OR Expansion (for De Morgan ORs)\n// ============================================================================\n\n/**\n * Expand OR conditions in exclusive entries AFTER buildExclusiveConditions.\n *\n * This handles ORs that arise from De Morgan expansion during negation:\n * !(A & B) = !A | !B\n *\n * These ORs need to be made exclusive to avoid overlapping CSS rules:\n * !A | !B → !A | (A & !B)\n *\n * This is logically equivalent but ensures each branch has proper context.\n *\n * Example:\n * Input: { \"\": V1, \"@supports(...) & :has()\": V2 }\n * V2's exclusive = @supports & :has\n * V1's exclusive = !(@supports & :has) = !@supports | !:has\n *\n * Without this fix: V1 gets two rules:\n * - @supports (not ...) → V1 ✓\n * - :not(:has()) → V1 ✗ (missing @supports context!)\n *\n * With this fix: V1 gets two exclusive rules:\n * - @supports (not ...) → V1 ✓\n * - @supports (...) { :not(:has()) } → V1 ✓ (proper context!)\n */\nexport function expandExclusiveOrs(\n entries: ExclusiveStyleEntry[],\n): ExclusiveStyleEntry[] {\n const result: ExclusiveStyleEntry[] = [];\n\n for (const entry of entries) {\n const expanded = expandExclusiveConditionOrs(entry);\n result.push(...expanded);\n }\n\n return result;\n}\n\n/**\n * Check if a condition involves at-rules (media, container, supports, starting)\n */\nfunction hasAtRuleContext(node: ConditionNode): boolean {\n if (node.kind === 'true' || node.kind === 'false') {\n return false;\n }\n\n if (node.kind === 'state') {\n // These condition types generate at-rules\n return (\n node.type === 'media' ||\n node.type === 'container' ||\n node.type === 'supports' ||\n node.type === 'starting'\n );\n }\n\n if (node.kind === 'compound') {\n return node.children.some(hasAtRuleContext);\n }\n\n return false;\n}\n\n/**\n * Sort OR branches to prioritize at-rule conditions first.\n *\n * This is critical for correct CSS generation. For `!A | !B` where A is at-rule\n * and B is modifier, we want:\n * - Branch 0: !A (at-rule negation - covers \"no @supports/media\" case)\n * - Branch 1: A & !B (modifier negation with at-rule context)\n *\n * If we process in wrong order (!B first), we'd get:\n * - Branch 0: !B (modifier negation WITHOUT at-rule context - WRONG!)\n * - Branch 1: B & !A (at-rule negation with modifier - incomplete coverage)\n */\nfunction sortOrBranchesForExpansion(\n branches: ConditionNode[],\n): ConditionNode[] {\n return [...branches].sort((a, b) => {\n const aHasAtRule = hasAtRuleContext(a);\n const bHasAtRule = hasAtRuleContext(b);\n\n // At-rule conditions come first\n if (aHasAtRule && !bHasAtRule) return -1;\n if (!aHasAtRule && bHasAtRule) return 1;\n\n // Same type - keep original order (stable sort)\n return 0;\n });\n}\n\n/**\n * Expand ORs in a single entry's exclusive condition\n */\nfunction expandExclusiveConditionOrs(\n entry: ExclusiveStyleEntry,\n): ExclusiveStyleEntry[] {\n let orBranches = collectOrBranches(entry.exclusiveCondition);\n\n // If no OR (single branch), return as-is\n if (orBranches.length <= 1) {\n return [entry];\n }\n\n // Sort branches so at-rule conditions come first\n // This ensures proper context inheritance during expansion\n orBranches = sortOrBranchesForExpansion(orBranches);\n\n // Make each OR branch exclusive from prior branches\n const result: ExclusiveStyleEntry[] = [];\n const priorBranches: ConditionNode[] = [];\n\n for (let i = 0; i < orBranches.length; i++) {\n const branch = orBranches[i];\n\n // Build: branch & !prior[0] & !prior[1] & ...\n // This transforms: !A | !B → !A, !B & !!A = !A, (A & !B)\n let exclusiveBranch: ConditionNode = branch;\n for (const prior of priorBranches) {\n exclusiveBranch = and(exclusiveBranch, not(prior));\n }\n\n // Simplify to detect impossible combinations and clean up double negations\n const simplified = simplifyCondition(exclusiveBranch);\n\n // Skip impossible branches\n if (simplified.kind === 'false') {\n priorBranches.push(branch);\n continue;\n }\n\n result.push({\n ...entry,\n stateKey: `${entry.stateKey}[or:${i}]`, // Mark as expanded OR branch\n exclusiveCondition: simplified,\n });\n\n priorBranches.push(branch);\n }\n\n return result;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"exclusive.js","names":[],"sources":["../../src/pipeline/exclusive.ts"],"sourcesContent":["/**\n * Exclusive Condition Builder\n *\n * Transforms parsed style entries into exclusive conditions.\n * Each entry's condition is ANDed with the negation of all higher-priority conditions,\n * ensuring exactly one condition matches at any given time.\n */\n\nimport type { StyleValue } from '../utils/styles';\n\nimport type { ConditionNode } from './conditions';\nimport { and, isCompoundCondition, not, or, trueCondition } from './conditions';\nimport { simplifyCondition } from './simplify';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Parsed style entry with condition\n */\nexport interface ParsedStyleEntry {\n styleKey: string; // e.g., 'padding', 'fill'\n stateKey: string; // Original key: '', 'compact', '@media(w < 768px)'\n value: StyleValue; // The style value (before handler processing)\n condition: ConditionNode; // Parsed condition tree\n priority: number; // Order in original object (higher = higher priority)\n}\n\n/**\n * Style entry with exclusive condition\n */\nexport interface ExclusiveStyleEntry extends ParsedStyleEntry {\n exclusiveCondition: ConditionNode; // condition & !higherPriorityConditions\n}\n\n// ============================================================================\n// Main Function\n// ============================================================================\n\n/**\n * Build exclusive conditions for a list of parsed style entries.\n *\n * The entries should be ordered by priority (highest priority first).\n *\n * For each entry, we compute:\n * exclusiveCondition = condition & !prior[0] & !prior[1] & ...\n *\n * This ensures exactly one condition matches at any time.\n *\n * Example:\n * Input (ordered highest to lowest priority):\n * A: value1 (priority 2)\n * B: value2 (priority 1)\n * C: value3 (priority 0)\n *\n * Output:\n * A: A\n * B: B & !A\n * C: C & !A & !B\n *\n * @param entries Parsed style entries ordered by priority (highest first)\n * @returns Entries with exclusive conditions, filtered to remove impossible ones\n */\nexport function buildExclusiveConditions(\n entries: ParsedStyleEntry[],\n): ExclusiveStyleEntry[] {\n const result: ExclusiveStyleEntry[] = [];\n const priorConditions: ConditionNode[] = [];\n\n for (const entry of entries) {\n // Build: condition & !prior[0] & !prior[1] & ...\n let exclusive: ConditionNode = entry.condition;\n\n for (const prior of priorConditions) {\n // Skip negating \"always true\" (default state) - it would become \"always false\"\n if (prior.kind !== 'true') {\n exclusive = and(exclusive, not(prior));\n }\n }\n\n // Simplify the exclusive condition\n const simplified = simplifyCondition(exclusive);\n\n // Skip impossible conditions (simplified to FALSE)\n if (simplified.kind === 'false') {\n continue;\n }\n\n result.push({\n ...entry,\n exclusiveCondition: simplified,\n });\n\n // Add non-default conditions to prior list for subsequent entries\n if (entry.condition.kind !== 'true') {\n priorConditions.push(entry.condition);\n }\n }\n\n return result;\n}\n\n/**\n * Parse style entries from a value mapping object.\n *\n * @param styleKey The style key (e.g., 'padding')\n * @param valueMap The value mapping { '': '2x', 'compact': '1x', '@media(w < 768px)': '0.5x' }\n * @param parseCondition Function to parse state keys into conditions\n * @returns Parsed entries ordered by priority (highest first)\n */\nexport function parseStyleEntries(\n styleKey: string,\n valueMap: Record<string, StyleValue>,\n parseCondition: (stateKey: string) => ConditionNode,\n): ParsedStyleEntry[] {\n const entries: ParsedStyleEntry[] = [];\n const keys = Object.keys(valueMap);\n\n keys.forEach((stateKey, index) => {\n const value = valueMap[stateKey];\n const condition =\n stateKey === '' ? trueCondition() : parseCondition(stateKey);\n\n entries.push({\n styleKey,\n stateKey,\n value,\n condition,\n priority: index,\n });\n });\n\n // Reverse so highest priority (last in object) comes first for exclusive building\n // buildExclusiveConditions expects highest priority first\n entries.reverse();\n\n return entries;\n}\n\n/**\n * Merge parsed entries that share the same value.\n *\n * When multiple **non-default** state keys map to the same value, their\n * conditions can be combined with OR and treated as a single entry.\n * This must happen **before** exclusive expansion and OR branch splitting\n * to avoid combinatorial explosion and duplicate CSS output.\n *\n * Default (TRUE) entries are **never** merged with non-default entries.\n * Merging `TRUE | X` collapses to `TRUE`, destroying the non-default\n * condition's participation in exclusive building. That causes\n * intermediate-priority states to lose their `:not(X)` negation,\n * breaking mutual exclusivity when X and an intermediate state are\n * both active. Stage 6 `mergeByValue` handles combining rules with\n * identical CSS output after exclusive conditions are correctly built.\n *\n * Example: `{ '@dark': 'red', '@dark & @hc': 'red' }` merges into a\n * single entry with condition `@dark | (@dark & @hc)` = `@dark`.\n *\n * Entries are ordered highest-priority-first. The merged entry keeps the\n * highest priority of the group.\n */\nexport function mergeEntriesByValue(\n entries: ParsedStyleEntry[],\n): ParsedStyleEntry[] {\n if (entries.length <= 1) return entries;\n\n const groups = new Map<\n string,\n { entries: ParsedStyleEntry[]; maxPriority: number }\n >();\n\n for (const entry of entries) {\n const valueKey = serializeValue(entry.value);\n const group = groups.get(valueKey);\n if (group) {\n group.entries.push(entry);\n group.maxPriority = Math.max(group.maxPriority, entry.priority);\n } else {\n groups.set(valueKey, { entries: [entry], maxPriority: entry.priority });\n }\n }\n\n // If no merges possible, return as-is\n if (groups.size === entries.length) return entries;\n\n const merged: ParsedStyleEntry[] = [];\n for (const [, group] of groups) {\n if (group.entries.length === 1) {\n merged.push(group.entries[0]);\n continue;\n }\n\n // Separate default (TRUE) entries from non-default entries.\n // Default entries must stay separate so that non-default conditions\n // participate in exclusive building and block intermediate states.\n const defaultEntries = group.entries.filter(\n (e) => e.condition.kind === 'true',\n );\n const nonDefaultEntries = group.entries.filter(\n (e) => e.condition.kind !== 'true',\n );\n\n // Keep default entries as-is\n for (const entry of defaultEntries) {\n merged.push(entry);\n }\n\n // Merge only non-default entries\n if (nonDefaultEntries.length === 1) {\n merged.push(nonDefaultEntries[0]);\n } else if (nonDefaultEntries.length >= 2) {\n const combinedCondition = simplifyCondition(\n or(...nonDefaultEntries.map((e) => e.condition)),\n );\n\n const combinedStateKey = nonDefaultEntries\n .map((e) => e.stateKey)\n .join(' | ');\n\n merged.push({\n styleKey: nonDefaultEntries[0].styleKey,\n stateKey: combinedStateKey,\n value: nonDefaultEntries[0].value,\n condition: combinedCondition,\n priority: group.maxPriority,\n });\n }\n }\n\n // Re-sort by priority (highest first)\n merged.sort((a, b) => b.priority - a.priority);\n\n return merged;\n}\n\nfunction serializeValue(value: StyleValue): string {\n if (value === null || value === undefined) return 'null';\n if (typeof value === 'string' || typeof value === 'number') {\n return String(value);\n }\n return JSON.stringify(value);\n}\n\n// ============================================================================\n// Compound State Extraction\n// ============================================================================\n\n/**\n * Eliminate redundant state dimensions from a value map.\n *\n * When a value map contains compound AND state keys (e.g. `@dark & @hc`),\n * checks whether any state atom is a \"don't-care\" variable — i.e. the\n * value is the same whether that atom is present or absent. Redundant\n * atoms are removed from all keys and duplicate entries are collapsed.\n *\n * This runs **before** condition parsing so that downstream stages\n * (`mergeEntriesByValue`, `buildExclusiveConditions`, materialization)\n * never see the irrelevant dimension, producing simpler, smaller CSS.\n *\n * Only pure top-level AND combinations are eligible. Keys that contain\n * `|`, `^`, or `,` at the top level are treated as opaque single atoms.\n *\n * @example\n * { '': A, '@dark': B, '@hc': A, '@dark & @hc': B }\n * // @hc is redundant → { '': A, '@dark': B }\n */\nexport function extractCompoundStates(\n valueMap: Record<string, StyleValue>,\n): Record<string, StyleValue> {\n const keys = Object.keys(valueMap);\n\n if (keys.length < 3 || !keys.some((k) => k.includes('&'))) {\n return valueMap;\n }\n\n const entries = keys.map((key) => {\n const atoms = splitTopLevelAnd(key);\n return {\n // null means the key has non-AND operators; treat the whole key\n // as a single opaque atom so it never matches partial pairs.\n atoms: atoms ?? [key],\n value: valueMap[key],\n };\n });\n\n const allAtoms = new Set<string>();\n for (const e of entries) {\n for (const a of e.atoms) allAtoms.add(a);\n }\n\n const redundant = new Set<string>();\n for (const atom of allAtoms) {\n if (isAtomRedundant(entries, atom)) {\n redundant.add(atom);\n }\n }\n\n if (redundant.size === 0) return valueMap;\n\n const newMap: Record<string, StyleValue> = {};\n for (const e of entries) {\n const filtered = e.atoms.filter((a) => !redundant.has(a));\n const newKey = filtered.join(' & ');\n if (!(newKey in newMap)) {\n newMap[newKey] = e.value;\n }\n }\n\n return newMap;\n}\n\n/**\n * Split a state key by top-level `&` operators.\n *\n * Returns `null` if the key contains `|`, `^`, or `,` at the top level\n * (making it ineligible for atom-level extraction).\n * Returns `[]` for the empty string (default key).\n */\nfunction splitTopLevelAnd(key: string): string[] | null {\n if (key === '') return [];\n\n const parts: string[] = [];\n let depth = 0;\n let current = '';\n\n for (const ch of key) {\n if (ch === '(' || ch === '[') depth++;\n else if (ch === ')' || ch === ']') depth--;\n\n if (depth === 0) {\n if (ch === '&') {\n const trimmed = current.trim();\n if (trimmed) parts.push(trimmed);\n current = '';\n continue;\n }\n if (ch === '|' || ch === '^' || ch === ',') {\n return null;\n }\n }\n\n current += ch;\n }\n\n const trimmed = current.trim();\n if (trimmed) parts.push(trimmed);\n\n return parts;\n}\n\n/**\n * An atom is redundant when every entry that contains it has a matching\n * partner (same remaining atoms, atom absent) with the same value.\n */\nfunction isAtomRedundant(\n entries: { atoms: string[]; value: StyleValue }[],\n atom: string,\n): boolean {\n const withAtom = entries.filter((e) => e.atoms.includes(atom));\n if (withAtom.length === 0) return false;\n\n for (const wa of withAtom) {\n const remaining = wa.atoms.filter((a) => a !== atom);\n\n const pair = entries.find(\n (e) =>\n !e.atoms.includes(atom) &&\n e.atoms.length === remaining.length &&\n remaining.every((r) => e.atoms.includes(r)),\n );\n\n if (!pair) return false;\n if (serializeValue(wa.value) !== serializeValue(pair.value)) return false;\n }\n\n return true;\n}\n\n/**\n * Check if a value is a style value mapping (object with state keys)\n */\nexport function isValueMapping(\n value: StyleValue | Record<string, StyleValue>,\n): value is Record<string, StyleValue> {\n return (\n value !== null &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n !(value instanceof Date)\n );\n}\n\n// ============================================================================\n// OR Expansion\n// ============================================================================\n\n/**\n * Expand OR conditions in parsed entries into multiple exclusive entries.\n *\n * For an entry with condition `A | B | C`, this creates 3 entries:\n * - condition: A\n * - condition: B & !A\n * - condition: C & !A & !B\n *\n * This ensures OR branches are mutually exclusive BEFORE the main\n * exclusive condition building pass.\n *\n * @param entries Parsed entries (may contain OR conditions)\n * @returns Expanded entries with OR branches made exclusive\n */\nexport function expandOrConditions(\n entries: ParsedStyleEntry[],\n): ParsedStyleEntry[] {\n const result: ParsedStyleEntry[] = [];\n\n for (const entry of entries) {\n const expanded = expandSingleEntry(entry);\n result.push(...expanded);\n }\n\n return result;\n}\n\n/**\n * Expand a single entry's OR condition into multiple exclusive entries.\n *\n * Note: branches are NOT sorted by at-rule context here (unlike the\n * `expandExclusiveOrs` pass below). User-authored ORs in state keys aren't\n * the product of De Morgan negation, so each branch is expected to render\n * independently in its own scope and at-rule sort isn't load-bearing.\n * The post-build pass needs the sort because it has to preserve at-rule\n * wrapping across branches that came from negating a compound at-rule.\n */\nfunction expandSingleEntry(entry: ParsedStyleEntry): ParsedStyleEntry[] {\n const orBranches = collectOrBranches(entry.condition);\n\n // If no OR (single branch), return as-is\n if (orBranches.length <= 1) {\n return [entry];\n }\n\n // Make each OR branch exclusive from prior branches\n const result: ParsedStyleEntry[] = [];\n const priorBranches: ConditionNode[] = [];\n\n for (let i = 0; i < orBranches.length; i++) {\n const branch = orBranches[i];\n\n // Build: branch & !prior[0] & !prior[1] & ...\n let exclusiveBranch: ConditionNode = branch;\n for (const prior of priorBranches) {\n exclusiveBranch = and(exclusiveBranch, not(prior));\n }\n\n // Simplify to detect impossible combinations\n const simplified = simplifyCondition(exclusiveBranch);\n\n // Skip impossible branches\n if (simplified.kind === 'false') {\n priorBranches.push(branch);\n continue;\n }\n\n result.push({\n ...entry,\n stateKey: `${entry.stateKey}[${i}]`, // Mark as expanded branch\n condition: simplified,\n // Keep same priority - all branches from same entry have same priority\n });\n\n priorBranches.push(branch);\n }\n\n return result;\n}\n\n/**\n * Collect top-level OR branches from a condition.\n *\n * For `A | B | C`, returns [A, B, C]\n * For `A & B`, returns [A & B] (single branch)\n * For `A | (B & C)`, returns [A, B & C]\n */\nfunction collectOrBranches(condition: ConditionNode): ConditionNode[] {\n if (condition.kind === 'true' || condition.kind === 'false') {\n return [condition];\n }\n\n if (isCompoundCondition(condition) && condition.operator === 'OR') {\n // Flatten nested ORs\n const branches: ConditionNode[] = [];\n for (const child of condition.children) {\n branches.push(...collectOrBranches(child));\n }\n return branches;\n }\n\n // Not an OR - return as single branch\n return [condition];\n}\n\n// ============================================================================\n// Post-Build OR Expansion (for De Morgan ORs)\n// ============================================================================\n\n/**\n * Expand OR conditions in exclusive entries AFTER buildExclusiveConditions.\n *\n * This handles ORs that arise from De Morgan expansion during negation:\n * !(A & B) = !A | !B\n *\n * These ORs need to be made exclusive to avoid overlapping CSS rules:\n * !A | !B → !A | (A & !B)\n *\n * This is logically equivalent but ensures each branch has proper context.\n *\n * Example:\n * Input: { \"\": V1, \"@supports(...) & :has()\": V2 }\n * V2's exclusive = @supports & :has\n * V1's exclusive = !(@supports & :has) = !@supports | !:has\n *\n * Without this fix: V1 gets two rules:\n * - @supports (not ...) → V1 ✓\n * - :not(:has()) → V1 ✗ (missing @supports context!)\n *\n * With this fix: V1 gets two exclusive rules:\n * - @supports (not ...) → V1 ✓\n * - @supports (...) { :not(:has()) } → V1 ✓ (proper context!)\n */\nexport function expandExclusiveOrs(\n entries: ExclusiveStyleEntry[],\n): ExclusiveStyleEntry[] {\n const result: ExclusiveStyleEntry[] = [];\n\n for (const entry of entries) {\n const expanded = expandExclusiveConditionOrs(entry);\n result.push(...expanded);\n }\n\n return result;\n}\n\n/**\n * Check if a condition involves at-rules (media, container, supports, starting)\n */\nfunction hasAtRuleContext(node: ConditionNode): boolean {\n if (node.kind === 'true' || node.kind === 'false') {\n return false;\n }\n\n if (node.kind === 'state') {\n // These condition types generate at-rules\n return (\n node.type === 'media' ||\n node.type === 'container' ||\n node.type === 'supports' ||\n node.type === 'starting'\n );\n }\n\n if (node.kind === 'compound') {\n return node.children.some(hasAtRuleContext);\n }\n\n return false;\n}\n\n/**\n * Sort OR branches to prioritize at-rule conditions first.\n *\n * This is critical for correct CSS generation. For `!A | !B` where A is at-rule\n * and B is modifier, we want:\n * - Branch 0: !A (at-rule negation - covers \"no @supports/media\" case)\n * - Branch 1: A & !B (modifier negation with at-rule context)\n *\n * If we process in wrong order (!B first), we'd get:\n * - Branch 0: !B (modifier negation WITHOUT at-rule context - WRONG!)\n * - Branch 1: B & !A (at-rule negation with modifier - incomplete coverage)\n */\nfunction sortOrBranchesForExpansion(\n branches: ConditionNode[],\n): ConditionNode[] {\n return [...branches].sort((a, b) => {\n const aHasAtRule = hasAtRuleContext(a);\n const bHasAtRule = hasAtRuleContext(b);\n\n // At-rule conditions come first\n if (aHasAtRule && !bHasAtRule) return -1;\n if (!aHasAtRule && bHasAtRule) return 1;\n\n // Same type - keep original order (stable sort)\n return 0;\n });\n}\n\n/**\n * Expand ORs in a single entry's exclusive condition\n */\nfunction expandExclusiveConditionOrs(\n entry: ExclusiveStyleEntry,\n): ExclusiveStyleEntry[] {\n let orBranches = collectOrBranches(entry.exclusiveCondition);\n\n // If no OR (single branch), return as-is\n if (orBranches.length <= 1) {\n return [entry];\n }\n\n // Sort branches so at-rule conditions come first\n // This ensures proper context inheritance during expansion\n orBranches = sortOrBranchesForExpansion(orBranches);\n\n // Make each OR branch exclusive from prior branches\n const result: ExclusiveStyleEntry[] = [];\n const priorBranches: ConditionNode[] = [];\n\n for (let i = 0; i < orBranches.length; i++) {\n const branch = orBranches[i];\n\n // Build: branch & !prior[0] & !prior[1] & ...\n // This transforms: !A | !B → !A, !B & !!A = !A, (A & !B)\n let exclusiveBranch: ConditionNode = branch;\n for (const prior of priorBranches) {\n exclusiveBranch = and(exclusiveBranch, not(prior));\n }\n\n // Simplify to detect impossible combinations and clean up double negations\n const simplified = simplifyCondition(exclusiveBranch);\n\n // Skip impossible branches\n if (simplified.kind === 'false') {\n priorBranches.push(branch);\n continue;\n }\n\n result.push({\n ...entry,\n stateKey: `${entry.stateKey}[or:${i}]`, // Mark as expanded OR branch\n exclusiveCondition: simplified,\n });\n\n priorBranches.push(branch);\n }\n\n return result;\n}\n"],"mappings":"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"}
@@ -13,17 +13,25 @@ import { parseStateKey } from "./parseStateKey.js";
13
13
  /**
14
14
  * Tasty Style Rendering Pipeline
15
15
  *
16
- * This is the main entrypoint for the new pipeline implementation.
17
- * It implements the complete flow from style objects to CSS rules.
18
- *
19
- * Pipeline stages:
20
- * 1. PARSE CONDITIONS - Parse state keys into ConditionNode trees
21
- * 2. BUILD EXCLUSIVE CONDITIONS - AND with negation of higher-priority conditions
22
- * 3. SIMPLIFY CONDITIONS - Apply boolean algebra, detect contradictions
23
- * 4. GROUP BY HANDLER - Collect styles per handler, compute combinations
24
- * 5. COMPUTE CSS VALUES - Call handlers to get CSS declarations
25
- * 6. MERGE BY VALUE - Merge rules with identical CSS output
26
- * 7. MATERIALIZE CSS - Convert conditions to CSS selectors + at-rules
16
+ * Main entrypoint for the style rendering pipeline. Transforms a `Styles`
17
+ * object into an array of `CSSRule` objects ready for DOM injection.
18
+ *
19
+ * Per-handler stages (see docs/pipeline.md for full detail):
20
+ * 0. PRE-PARSE NORMALIZATION - extractCompoundStates (exclusive.ts)
21
+ * 1. PARSE CONDITIONS - parseStyleEntries + parseStateKey
22
+ * 1b. MERGE ENTRIES BY VALUE - mergeEntriesByValue (exclusive.ts)
23
+ * 2a. EXPAND USER OR BRANCHES - expandOrConditions (exclusive.ts)
24
+ * 2b. BUILD EXCLUSIVE CONDITIONS - buildExclusiveConditions
25
+ * 3. EXPAND DE MORGAN ORs - expandExclusiveOrs (exclusive.ts)
26
+ * 4. COMPUTE STATE COMBINATIONS - computeStateCombinations
27
+ * 5. CALL HANDLERS - run style handlers for each snapshot
28
+ * 6. MERGE BY VALUE - mergeByValue (index.ts)
29
+ * 7. MATERIALIZE CSS - conditionToCSS + materializeComputedRule
30
+ *
31
+ * Simplification (`simplifyCondition`) runs inside most stages; calls are
32
+ * memoized by condition unique-id. The post-pass in `runPipeline` dedupes
33
+ * identical rules and emits all `@starting-style` rules last so they win
34
+ * the cascade over their equal-specificity normal counterparts.
27
35
  */
28
36
  const pipelineCache = new Lru(5e3);
29
37
  /**
@@ -56,12 +64,25 @@ function runPipeline(styles, parserContext) {
56
64
  return normal.concat(starting);
57
65
  }
58
66
  /**
59
- * Process styles at a given nesting level
67
+ * Process styles at a given nesting level.
68
+ *
69
+ * Splits keys into nested-selector keys and style-handler keys, recurses
70
+ * into nested selectors, then runs the per-handler stages 1–7 over the
71
+ * style keys.
60
72
  */
61
73
  function processStyles(styles, selectorSuffix, parserContext, allRules) {
62
74
  const keys = Object.keys(styles);
63
75
  const selectorKeys = keys.filter((key) => isSelector(key));
64
76
  const styleKeys = keys.filter((key) => !isSelector(key) && !key.startsWith("@"));
77
+ processNestedSelectors(styles, selectorKeys, selectorSuffix, parserContext, allRules);
78
+ processHandlerQueue(buildHandlerQueue(styleKeys, styles), selectorSuffix, parserContext, allRules);
79
+ }
80
+ /**
81
+ * Recurse into nested selector keys. Each nested key may expand into multiple
82
+ * suffixes (comma-separated patterns); each suffix is processed independently
83
+ * with the parent's parser context augmented for sub-element scope.
84
+ */
85
+ function processNestedSelectors(styles, selectorKeys, selectorSuffix, parserContext, allRules) {
65
86
  for (const key of selectorKeys) {
66
87
  const nestedStyles = styles[key];
67
88
  if (!nestedStyles || typeof nestedStyles !== "object") continue;
@@ -80,46 +101,15 @@ function processStyles(styles, selectorSuffix, parserContext, allRules) {
80
101
  };
81
102
  for (const suffix of suffixes) processStyles(cleanedStyles, selectorSuffix + suffix, subContext, allRules);
82
103
  }
83
- const handlerQueue = buildHandlerQueue(styleKeys, styles);
104
+ }
105
+ /**
106
+ * Run the per-handler pipeline (stages 1–7) over a handler queue and append
107
+ * the resulting CSS rules to `allRules`.
108
+ */
109
+ function processHandlerQueue(handlerQueue, selectorSuffix, parserContext, allRules) {
84
110
  for (const { handler, styleMap } of handlerQueue) {
85
111
  const lookupStyles = handler.__lookupStyles;
86
- const exclusiveByStyle = /* @__PURE__ */ new Map();
87
- for (const styleName of lookupStyles) {
88
- const value = styleMap[styleName];
89
- if (value === void 0) continue;
90
- if (isValueMapping(value)) {
91
- const fullyExpanded = expandExclusiveOrs(buildExclusiveConditions(expandOrConditions(mergeEntriesByValue(parseStyleEntries(styleName, extractCompoundStates(value), (stateKey) => parseStateKey(stateKey, { context: parserContext }))))));
92
- exclusiveByStyle.set(styleName, fullyExpanded);
93
- } else exclusiveByStyle.set(styleName, [{
94
- styleKey: styleName,
95
- stateKey: "",
96
- value,
97
- condition: trueCondition(),
98
- priority: 0,
99
- exclusiveCondition: trueCondition()
100
- }]);
101
- }
102
- const stateSnapshots = computeStateCombinations(exclusiveByStyle, lookupStyles);
103
- const computedRules = [];
104
- for (const snapshot of stateSnapshots) {
105
- const result = handler(snapshot.values);
106
- if (!result) continue;
107
- const results = Array.isArray(result) ? result : [result];
108
- for (const r of results) {
109
- if (!r || typeof r !== "object") continue;
110
- const { $, ...styleProps } = r;
111
- const declarations = {};
112
- for (const [prop, val] of Object.entries(styleProps)) if (val != null && val !== "") declarations[prop] = String(val);
113
- if (Object.keys(declarations).length === 0) continue;
114
- const suffixes = $ ? (Array.isArray($) ? $ : [$]).map((s) => selectorSuffix + normalizeSelectorSuffix(String(s))) : [selectorSuffix];
115
- for (const suffix of suffixes) computedRules.push({
116
- condition: snapshot.condition,
117
- declarations,
118
- selectorSuffix: suffix
119
- });
120
- }
121
- }
122
- const mergedRules = mergeByValue(computedRules);
112
+ const mergedRules = mergeByValue(invokeHandler(handler, computeStateCombinations(buildExclusivesForHandler(lookupStyles, styleMap, parserContext), lookupStyles), selectorSuffix));
123
113
  for (const rule of mergedRules) {
124
114
  const cssRules = materializeComputedRule(rule);
125
115
  allRules.push(...cssRules);
@@ -127,6 +117,57 @@ function processStyles(styles, selectorSuffix, parserContext, allRules) {
127
117
  }
128
118
  }
129
119
  /**
120
+ * Stages 0–3 for a single handler: take the handler's looked-up style names,
121
+ * resolve each style's value map into a list of mutually-exclusive entries.
122
+ * Simple non-mapping values produce a single TRUE-conditioned entry.
123
+ */
124
+ function buildExclusivesForHandler(lookupStyles, styleMap, parserContext) {
125
+ const exclusiveByStyle = /* @__PURE__ */ new Map();
126
+ for (const styleName of lookupStyles) {
127
+ const value = styleMap[styleName];
128
+ if (value === void 0) continue;
129
+ if (isValueMapping(value)) {
130
+ const fullyExpanded = expandExclusiveOrs(buildExclusiveConditions(expandOrConditions(mergeEntriesByValue(parseStyleEntries(styleName, extractCompoundStates(value), (stateKey) => parseStateKey(stateKey, { context: parserContext }))))));
131
+ exclusiveByStyle.set(styleName, fullyExpanded);
132
+ } else exclusiveByStyle.set(styleName, [{
133
+ styleKey: styleName,
134
+ stateKey: "",
135
+ value,
136
+ condition: trueCondition(),
137
+ priority: 0,
138
+ exclusiveCondition: trueCondition()
139
+ }]);
140
+ }
141
+ return exclusiveByStyle;
142
+ }
143
+ /**
144
+ * Stage 5: invoke the handler for each state snapshot and translate its
145
+ * return value into ComputedRule entries (one per declaration set, fanned
146
+ * out across any `$` selector suffixes the handler returns).
147
+ */
148
+ function invokeHandler(handler, stateSnapshots, selectorSuffix) {
149
+ const computedRules = [];
150
+ for (const snapshot of stateSnapshots) {
151
+ const result = handler(snapshot.values);
152
+ if (!result) continue;
153
+ const results = Array.isArray(result) ? result : [result];
154
+ for (const r of results) {
155
+ if (!r || typeof r !== "object") continue;
156
+ const { $, ...styleProps } = r;
157
+ const declarations = {};
158
+ for (const [prop, val] of Object.entries(styleProps)) if (val != null && val !== "") declarations[prop] = String(val);
159
+ if (Object.keys(declarations).length === 0) continue;
160
+ const suffixes = $ ? (Array.isArray($) ? $ : [$]).map((s) => selectorSuffix + normalizeSelectorSuffix(String(s))) : [selectorSuffix];
161
+ for (const suffix of suffixes) computedRules.push({
162
+ condition: snapshot.condition,
163
+ declarations,
164
+ selectorSuffix: suffix
165
+ });
166
+ }
167
+ }
168
+ return computedRules;
169
+ }
170
+ /**
130
171
  * Check if a key is a CSS selector
131
172
  */
132
173
  function isSelector(key) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/pipeline/index.ts"],"sourcesContent":["/**\n * Tasty Style Rendering Pipeline\n *\n * This is the main entrypoint for the new pipeline implementation.\n * It implements the complete flow from style objects to CSS rules.\n *\n * Pipeline stages:\n * 1. PARSE CONDITIONS - Parse state keys into ConditionNode trees\n * 2. BUILD EXCLUSIVE CONDITIONS - AND with negation of higher-priority conditions\n * 3. SIMPLIFY CONDITIONS - Apply boolean algebra, detect contradictions\n * 4. GROUP BY HANDLER - Collect styles per handler, compute combinations\n * 5. COMPUTE CSS VALUES - Call handlers to get CSS declarations\n * 6. MERGE BY VALUE - Merge rules with identical CSS output\n * 7. MATERIALIZE CSS - Convert conditions to CSS selectors + at-rules\n */\n\nimport { Lru } from '../parser/lru';\nimport type { StateParserContext } from '../states';\nimport {\n createStateParserContext,\n extractLocalPredefinedStates,\n} from '../states';\nimport { createStyle, STYLE_HANDLER_MAP } from '../styles';\nimport type { Styles } from '../styles/types';\nimport type {\n StyleHandler,\n StyleMap,\n StyleValue,\n StyleValueStateMap,\n} from '../utils/styles';\nimport { stringifyStyles } from '../utils/styles';\n\nimport type { ConditionNode } from './conditions';\nimport { and, or, trueCondition } from './conditions';\nimport type { ExclusiveStyleEntry } from './exclusive';\nimport {\n buildExclusiveConditions,\n expandExclusiveOrs,\n expandOrConditions,\n extractCompoundStates,\n isValueMapping,\n mergeEntriesByValue,\n parseStyleEntries,\n} from './exclusive';\nimport type { CSSRule, SelectorVariant } from './materialize';\nimport {\n branchToCSS,\n buildAtRulesFromVariant,\n conditionToCSS,\n mergeVariantsIntoSelectorGroups,\n optimizeGroups,\n parentGroupsToCSS,\n rootGroupsToCSS,\n selectorGroupToCSS,\n} from './materialize';\nimport { parseStateKey } from './parseStateKey';\nimport { simplifyCondition } from './simplify';\nimport { emitWarning } from './warnings';\n\n// ============================================================================\n// Types (compatible with old renderStyles API)\n// ============================================================================\n\n/**\n * Matches the old StyleResult interface for backward compatibility\n */\nexport interface StyleResult {\n selector: string;\n declarations: string;\n atRules?: string[];\n needsClassName?: boolean;\n rootPrefix?: string;\n /** When true, declarations are wrapped in @starting-style { ... } inside the selector rule */\n startingStyle?: boolean;\n}\n\n/**\n * Matches the old RenderResult interface for backward compatibility\n */\nexport interface RenderResult {\n rules: StyleResult[];\n className?: string;\n}\n\nexport interface PipelineResult {\n rules: CSSRule[];\n className?: string;\n}\n\ninterface ComputedRule {\n condition: ConditionNode;\n declarations: Record<string, string>;\n selectorSuffix: string;\n}\n\n// ============================================================================\n// Caching\n// ============================================================================\n\nconst pipelineCache = new Lru<string, CSSRule[]>(5000);\n\n// ============================================================================\n// Main Pipeline Function\n// ============================================================================\n\n/**\n * Render styles using the new pipeline.\n *\n * This is the main entrypoint that implements the complete flow.\n */\nexport function renderStylesPipeline(\n styles?: Styles,\n className?: string,\n pipelineCacheKey?: string,\n): PipelineResult {\n if (!styles) {\n return { rules: [], className };\n }\n\n // Use pre-computed cache key when available, falling back to stringifyStyles\n const cacheKey = pipelineCacheKey || stringifyStyles(styles);\n let rules = pipelineCache.get(cacheKey);\n\n if (!rules) {\n // Create parser context\n const parserContext = createStateParserContext(styles);\n\n // Run pipeline\n rules = runPipeline(styles, parserContext);\n\n // Cache result\n pipelineCache.set(cacheKey, rules);\n }\n\n // If no className, rules need it to be prepended later\n if (!className) {\n return {\n rules: rules.map((r) => ({\n ...r,\n needsClassName: true,\n })),\n };\n }\n\n // Prepend className to selectors\n const finalRules = rules.map((rule) => {\n // Parse the selector to find where to insert className\n let selector = rule.selector;\n\n // If selector starts with :root, insert className after the :root part\n if (rule.rootPrefix) {\n selector = `${rule.rootPrefix} .${className}.${className}${selector}`;\n } else {\n selector = `.${className}.${className}${selector}`;\n }\n\n return {\n ...rule,\n selector,\n };\n });\n\n return {\n rules: finalRules,\n className,\n };\n}\n\n/**\n * Check if a cache key exists in the pipeline cache.\n * Used by renderStylesForChunk to avoid building filtered styles on cache hit.\n */\nexport function hasPipelineCacheEntry(cacheKey: string): boolean {\n return pipelineCache.get(cacheKey) !== undefined;\n}\n\n/**\n * Clear the pipeline cache (for testing)\n */\nexport function clearPipelineCache(): void {\n pipelineCache.clear();\n}\n\n// ============================================================================\n// Pipeline Implementation\n// ============================================================================\n\nfunction runPipeline(\n styles: Styles,\n parserContext: StateParserContext,\n): CSSRule[] {\n const allRules: CSSRule[] = [];\n\n // Process styles recursively (including nested selectors)\n processStyles(styles, '', parserContext, allRules);\n\n // Deduplicate rules\n const seen = new Set<string>();\n const dedupedRules = allRules.filter((rule) => {\n const key = `${rule.selector}|${rule.declarations}|${rule.atRules?.join('|') ?? ''}|${rule.rootPrefix || ''}|${rule.startingStyle ? '1' : '0'}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n\n // @starting-style rules must come AFTER normal rules for the same selector.\n // They share the same specificity, so source order decides the cascade.\n // If a @starting-style rule appears before its normal counterpart,\n // the later normal rule overrides the starting value.\n const normal: CSSRule[] = [];\n const starting: CSSRule[] = [];\n\n for (const rule of dedupedRules) {\n if (rule.startingStyle) {\n starting.push(rule);\n } else {\n normal.push(rule);\n }\n }\n\n return normal.concat(starting);\n}\n\n/**\n * Process styles at a given nesting level\n */\nfunction processStyles(\n styles: Styles,\n selectorSuffix: string,\n parserContext: StateParserContext,\n allRules: CSSRule[],\n): void {\n const keys = Object.keys(styles);\n\n // Separate selector keys from style keys\n // Skip @keyframes (processed separately) and other @ prefixed keys (predefined states)\n const selectorKeys = keys.filter((key) => isSelector(key));\n const styleKeys = keys.filter(\n (key) => !isSelector(key) && !key.startsWith('@'),\n );\n\n // Process nested selectors first\n for (const key of selectorKeys) {\n const nestedStyles = styles[key] as Styles;\n if (!nestedStyles || typeof nestedStyles !== 'object') continue;\n\n // Get all selectors (handles comma-separated patterns)\n const suffixes = getAllSelectors(key, nestedStyles);\n if (!suffixes) continue; // Invalid selector, skip\n\n // Remove $ from nested styles\n const { $: _$, ...cleanedStyles } = nestedStyles;\n\n // Extract local predefined states scoped to this sub-element\n const subLocalStates = extractLocalPredefinedStates(cleanedStyles);\n const hasSubStates = Object.keys(subLocalStates).length > 0;\n const subContext: StateParserContext = {\n ...parserContext,\n isSubElement: true,\n localPredefinedStates: hasSubStates\n ? { ...parserContext.localPredefinedStates, ...subLocalStates }\n : parserContext.localPredefinedStates,\n };\n\n // Process for each selector (multiple selectors = same styles applied to each)\n for (const suffix of suffixes) {\n processStyles(\n cleanedStyles,\n selectorSuffix + suffix,\n subContext,\n allRules,\n );\n }\n }\n\n // Build handler queue\n const handlerQueue = buildHandlerQueue(styleKeys, styles);\n\n // Process each handler\n for (const { handler, styleMap } of handlerQueue) {\n const lookupStyles = handler.__lookupStyles;\n\n // Stage 1 & 2: Parse and build exclusive conditions for each style\n // Exclusive conditions ensure each CSS rule applies to exactly one state.\n // OR conditions in exclusives are properly expanded to DNF (multiple CSS selectors).\n const exclusiveByStyle = new Map<string, ExclusiveStyleEntry[]>();\n\n for (const styleName of lookupStyles) {\n const value = styleMap[styleName];\n if (value === undefined) continue;\n\n if (isValueMapping(value)) {\n // Eliminate redundant compound state dimensions before parsing.\n // E.g. { '': A, '@dark': B, '@hc': A, '@dark & @hc': B }\n // reduces to { '': A, '@dark': B } because @hc is irrelevant.\n const reduced = extractCompoundStates(\n value as Record<string, StyleValue>,\n );\n\n // Parse entries from value mapping\n const parsed = parseStyleEntries(styleName, reduced, (stateKey) =>\n parseStateKey(stateKey, { context: parserContext }),\n );\n\n // Merge entries that share the same value before exclusive\n // expansion. This prevents combinatorial blowup when e.g.\n // @dark and @dark & @high-contrast map to the same color.\n const merged = mergeEntriesByValue(parsed);\n\n // Expand OR conditions into exclusive branches\n // This ensures OR branches like `A | B | C` become:\n // A, B & !A, C & !A & !B\n const expanded = expandOrConditions(merged);\n\n // Build exclusive conditions across all entries\n const exclusive = buildExclusiveConditions(expanded);\n\n // Expand ORs from De Morgan negation into exclusive branches\n // This transforms: !A | !B → !A, (A & !B)\n // Ensures each CSS rule has proper at-rule context\n const fullyExpanded = expandExclusiveOrs(exclusive);\n exclusiveByStyle.set(styleName, fullyExpanded);\n } else {\n // Simple value - single entry with TRUE condition\n exclusiveByStyle.set(styleName, [\n {\n styleKey: styleName,\n stateKey: '',\n value,\n condition: trueCondition(),\n priority: 0,\n exclusiveCondition: trueCondition(),\n },\n ]);\n }\n }\n\n // Stage 4: Compute all valid state combinations\n const stateSnapshots = computeStateCombinations(\n exclusiveByStyle,\n lookupStyles,\n );\n\n // Stage 5: Call handler for each snapshot\n const computedRules: ComputedRule[] = [];\n\n for (const snapshot of stateSnapshots) {\n const result = handler(snapshot.values as StyleValueStateMap);\n if (!result) continue;\n\n // Handler may return single or array\n const results = Array.isArray(result) ? result : [result];\n\n for (const r of results) {\n if (!r || typeof r !== 'object') continue;\n\n const { $, ...styleProps } = r;\n const declarations: Record<string, string> = {};\n\n for (const [prop, val] of Object.entries(styleProps)) {\n if (val != null && val !== '') {\n declarations[prop] = String(val);\n }\n }\n\n if (Object.keys(declarations).length === 0) continue;\n\n // Handle $ suffixes\n const suffixes = $\n ? (Array.isArray($) ? $ : [$]).map(\n (s) => selectorSuffix + normalizeSelectorSuffix(String(s)),\n )\n : [selectorSuffix];\n\n for (const suffix of suffixes) {\n computedRules.push({\n condition: snapshot.condition,\n declarations,\n selectorSuffix: suffix,\n });\n }\n }\n }\n\n // Stage 6: Merge rules with identical CSS output\n const mergedRules = mergeByValue(computedRules);\n\n // Stage 7: Materialize to CSS\n for (const rule of mergedRules) {\n const cssRules = materializeComputedRule(rule);\n allRules.push(...cssRules);\n }\n }\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Check if a key is a CSS selector\n */\nexport function isSelector(key: string): boolean {\n return key.startsWith('&') || key.startsWith('.') || /^[A-Z]/.test(key);\n}\n\n/**\n * Result of processing a selector affix ($) pattern.\n *\n * @example\n * // Valid result with multiple selectors\n * { valid: true, selectors: ['> [data-element=\"Cell\"]', ' [data-element=\"Body\"] > [data-element=\"Cell\"]'] }\n *\n * // Invalid result with error message\n * { valid: false, reason: 'Selector affix \"+\" targets elements outside the root scope.' }\n */\ntype AffixResult =\n | { valid: true; selectors: string[] }\n | { valid: false; reason: string };\n\n/**\n * Get all selector suffixes for a sub-element key.\n *\n * Handles three types of selector keys:\n * - `&` prefix: Raw selector suffix (e.g., `&:hover` → `:hover`)\n * - `.` prefix: Class selector (e.g., `.active` → ` .active`)\n * - Uppercase: Sub-element with optional `$` affix pattern\n *\n * @param key - The sub-element key (e.g., 'Label', '&:hover', '.active')\n * @param styles - The styles object, may contain `$` property for selector affix\n * @returns Array of selector suffixes, or null if invalid (with console warning)\n *\n * @example\n * getAllSelectors('Label', {})\n * // → [' [data-element=\"Label\"]']\n *\n * getAllSelectors('Cell', { $: '>, >Body>' })\n * // → ['> [data-element=\"Cell\"]', ' [data-element=\"Body\"] > [data-element=\"Cell\"]']\n */\nfunction getAllSelectors(key: string, styles?: Styles): string[] | null {\n if (key.startsWith('&')) {\n return [key.slice(1)];\n }\n\n if (key.startsWith('.')) {\n return [` ${key}`];\n }\n\n if (/^[A-Z]/.test(key)) {\n const affix = styles?.$;\n if (affix !== undefined) {\n const result = processAffix(String(affix), key);\n if (!result.valid) {\n emitWarning('INVALID_SELECTOR_AFFIX', result.reason);\n return null;\n }\n return result.selectors;\n }\n return [` [data-element=\"${key}\"]`];\n }\n\n return null;\n}\n\n/**\n * Process selector affix pattern and return selector(s)\n *\n * Supports:\n * - Direct child: '>'\n * - Chained elements: '>Body>Row>'\n * - HTML tags (no key injection): 'h1', '>ul>li', 'button:hover'\n * - Universal selector: '*', 'h1 *'\n * - Pseudo-elements on root: '::before'\n * - Pseudo on sub-element: '@::before', '>@:hover'\n * - Classes: '.active', '>@.active'\n * - Multiple selectors: '>, >Body>'\n * - Sibling combinators (after element): '>Item+', '>Item~'\n */\nfunction processAffix(affix: string, key: string): AffixResult {\n const trimmed = affix.trim();\n\n // Empty = default behavior (descendant selector with key)\n if (!trimmed) {\n return { valid: true, selectors: [` [data-element=\"${key}\"]`] };\n }\n\n // Split by comma for multiple selectors\n const patterns = trimmed.split(',').map((p) => p.trim());\n const selectors: string[] = [];\n\n for (const pattern of patterns) {\n const validation = validatePattern(pattern);\n if (!validation.valid) {\n return validation;\n }\n\n const selector = processSinglePattern(pattern, key);\n selectors.push(selector);\n }\n\n return { valid: true, selectors };\n}\n\n/**\n * Recognized token patterns for selector affix validation.\n *\n * These patterns are used to tokenize and validate `$` affix strings.\n * Order matters: more specific patterns must come first to avoid\n * partial matches (e.g., `::before` must match before `:` alone).\n *\n * Unrecognized tokens (like `#id`, `*`, or numbers) will cause validation to fail.\n */\nconst VALID_TOKEN_PATTERNS = [\n /^[>+~]/, // Combinators: >, +, ~\n /^\\*/, // Universal selector (*)\n /^[A-Z][a-zA-Z0-9]*/, // Uppercase element names → [data-element=\"...\"]\n /^@/, // @ placeholder for key injection position\n /^::?[a-z][a-z0-9-]*(?:\\([^)]*\\))?/, // Pseudo-elements/classes (:hover, ::before, :not(.x))\n /^\\.[a-zA-Z_-][a-zA-Z0-9_-]*/, // Class selectors (.active, .is-open)\n /^\\[[^\\]]+\\]/, // Attribute selectors ([type=\"text\"], [role])\n /^[a-z][a-z0-9-]*/, // HTML tag names (a, div, button, my-component)\n /^\\s+/, // Whitespace (ignored during parsing)\n /^&/, // Root reference (stripped, kept for backward compat)\n];\n\n/**\n * Scan a pattern for unrecognized tokens.\n *\n * Iterates through the pattern, consuming recognized tokens until\n * either the pattern is fully consumed (valid) or an unrecognized\n * character sequence is found (invalid).\n *\n * @param pattern - The selector pattern to validate\n * @returns The first unrecognized token found, or null if all tokens are valid\n *\n * @example\n * findUnrecognizedTokens('>Body>Row>') // → null (valid)\n * findUnrecognizedTokens('123') // → '123' (invalid)\n * findUnrecognizedTokens('#myId') // → '#' (invalid)\n */\nfunction findUnrecognizedTokens(pattern: string): string | null {\n let remaining = pattern;\n\n while (remaining.length > 0) {\n let matched = false;\n\n for (const regex of VALID_TOKEN_PATTERNS) {\n const match = remaining.match(regex);\n if (match) {\n remaining = remaining.slice(match[0].length);\n matched = true;\n break;\n }\n }\n\n if (!matched) {\n // Found unrecognized content - extract the problematic part\n const unrecognized = remaining.match(/^[^\\s>+~@.:[\\]A-Z]+/);\n return unrecognized ? unrecognized[0] : remaining[0];\n }\n }\n\n return null;\n}\n\n/**\n * Validate a selector pattern for structural correctness.\n *\n * Checks for:\n * 1. Out-of-scope selectors: Patterns starting with `+` or `~` target siblings\n * of the root element, which is outside the component's DOM scope.\n * 2. Consecutive combinators: Patterns like `>>` or `>+` are malformed CSS.\n * 3. Unrecognized tokens: Characters/sequences not matching valid CSS selectors.\n *\n * @param pattern - A single selector pattern (already split by comma)\n * @returns AffixResult indicating validity and error reason if invalid\n *\n * @example\n * validatePattern('>Body>Row>') // → { valid: true, selectors: [] }\n * validatePattern('+') // → { valid: false, reason: '...outside root scope...' }\n * validatePattern('>>') // → { valid: false, reason: '...consecutive combinators...' }\n */\nfunction validatePattern(pattern: string): AffixResult {\n const trimmed = pattern.trim();\n\n // Patterns starting with + or ~ target siblings of the root element,\n // which is outside the component's scope. Valid sibling patterns must\n // be preceded by an element: \">Item+\", \">Item~\"\n if (/^[+~]/.test(trimmed)) {\n return {\n valid: false,\n reason:\n `Selector affix \"${pattern}\" targets elements outside the root scope. ` +\n `Sibling selectors (+, ~) must be preceded by an element inside the root. ` +\n `Use \">Element+\" or \">Element~\" instead.`,\n };\n }\n\n // Check for consecutive combinators\n if (/[>+~]{2,}/.test(trimmed.replace(/\\s+/g, ''))) {\n return {\n valid: false,\n reason: `Selector affix \"${pattern}\" contains consecutive combinators.`,\n };\n }\n\n // Check for unrecognized tokens (e.g., lowercase text like \"foo\")\n const unrecognized = findUnrecognizedTokens(trimmed);\n if (unrecognized) {\n return {\n valid: false,\n reason:\n `Selector affix \"${pattern}\" contains unrecognized token \"${unrecognized}\". ` +\n `Valid tokens: combinators (>, +, ~), element names (Uppercase), ` +\n `@ placeholder, pseudo (:hover, ::before), class (.name), attribute ([attr]).`,\n };\n }\n\n return { valid: true, selectors: [] };\n}\n\n/**\n * Process a single selector pattern into a CSS selector suffix.\n *\n * This is the main transformation function that converts a `$` affix pattern\n * into a valid CSS selector suffix. It handles:\n *\n * 1. `@` placeholder replacement with `[data-element=\"key\"]`\n * 2. Key injection based on pattern ending (see `shouldInjectKey`)\n * 3. Proper spacing for descendant vs direct child selectors\n *\n * @param pattern - A single validated selector pattern\n * @param key - The sub-element key to inject (e.g., 'Label', 'Cell')\n * @returns CSS selector suffix ready to append to the root selector\n *\n * @example\n * processSinglePattern('>', 'Row')\n * // → '> [data-element=\"Row\"]'\n *\n * processSinglePattern('>Body>Row>', 'Cell')\n * // → '> [data-element=\"Body\"] > [data-element=\"Row\"] > [data-element=\"Cell\"]'\n *\n * processSinglePattern('::before', 'Before')\n * // → '::before' (no key injection for pseudo on root)\n *\n * processSinglePattern('>@:hover', 'Item')\n * // → '> [data-element=\"Item\"]:hover'\n */\nfunction processSinglePattern(pattern: string, key: string): string {\n // Strip leading & if present (implicit root reference, kept for compat)\n const normalized = pattern.replace(/^&/, '').trim();\n\n if (!normalized) {\n return ` [data-element=\"${key}\"]`;\n }\n\n // Pseudo-elements/classes at start apply directly to root (no space prefix)\n const startsWithPseudo = /^::?[a-z]/.test(normalized);\n\n // Transform the pattern: convert element names and normalize spacing\n let result = transformPattern(normalized);\n\n // Handle @ placeholder: explicit key injection position\n if (result.includes('@')) {\n // Remove space between @ and following class/pseudo for proper attachment\n // e.g., \"@ .active\" → \"[el].active\", but \"@ > span\" → \"[el] > span\"\n result = result.replace(/@ (?=[.:])/g, '@');\n result = result.replace(/@/g, `[data-element=\"${key}\"]`);\n\n if (!startsWithPseudo && !result.startsWith(' ')) {\n result = ' ' + result;\n }\n return result;\n }\n\n // Auto-inject key based on pattern ending (see shouldInjectKey for rules)\n if (shouldInjectKey(normalized)) {\n result = result + ' ' + `[data-element=\"${key}\"]`;\n }\n\n // Add space prefix for selectors targeting inside root (not pseudo on root)\n if (!startsWithPseudo && !result.startsWith(' ')) {\n result = ' ' + result;\n }\n\n return result;\n}\n\n/**\n * Transform a selector pattern by converting element names and normalizing spacing.\n *\n * This is a character-by-character tokenizer that:\n * - Converts uppercase names to `[data-element=\"Name\"]` selectors\n * - Adds proper spacing around combinators (>, +, ~)\n * - Preserves lowercase tags, classes, pseudos, and attributes as-is\n * - Keeps @ placeholder for later replacement\n *\n * The tokenizer handles these token types in order:\n * 1. Whitespace (skipped)\n * 2. Combinators: >, +, ~ (add surrounding spaces)\n * 3. Universal selector: * (keep as-is with spacing)\n * 4. Uppercase names: Body, Row (convert to [data-element=\"...\"])\n * 5. @ placeholder (keep for later replacement)\n * 6. Pseudo: :hover, ::before (attach to previous token)\n * 7. Tags: a, div, button (keep as-is with spacing)\n * 8. Classes: .active (attach to previous element/tag/placeholder)\n * 9. Attributes: [type=\"text\"] (keep as-is)\n *\n * @param pattern - The raw selector pattern to transform\n * @returns Transformed pattern with proper CSS selector syntax\n *\n * @example\n * transformPattern('>Body>Row>')\n * // → '> [data-element=\"Body\"] > [data-element=\"Row\"] >'\n *\n * transformPattern('button.primary:hover')\n * // → 'button.primary:hover'\n */\nfunction transformPattern(pattern: string): string {\n let result = '';\n let lastCh = '';\n let i = 0;\n\n while (i < pattern.length) {\n const char = pattern[i];\n\n if (/\\s/.test(char)) {\n i++;\n continue;\n }\n\n if (/[>+~]/.test(char)) {\n if (result && lastCh !== ' ') {\n result += ' ';\n }\n result += char;\n lastCh = char;\n i++;\n continue;\n }\n\n if (char === '*') {\n if (result && lastCh !== ' ') {\n result += ' ';\n }\n result += '*';\n lastCh = '*';\n i++;\n continue;\n }\n\n if (/[A-Z]/.test(char)) {\n const nameStart = i;\n while (i < pattern.length && /[a-zA-Z0-9]/.test(pattern[i])) {\n i++;\n }\n if (result && lastCh !== ' ') {\n result += ' ';\n }\n const segment = `[data-element=\"${pattern.slice(nameStart, i)}\"]`;\n result += segment;\n lastCh = ']';\n continue;\n }\n\n if (char === '@') {\n if (result && lastCh !== ' ') {\n result += ' ';\n }\n result += '@';\n lastCh = '@';\n i++;\n continue;\n }\n\n if (char === ':') {\n const pseudoStart = i;\n while (\n i < pattern.length &&\n !/[\\s>+~,@]/.test(pattern[i]) &&\n !/[A-Z]/.test(pattern[i])\n ) {\n i++;\n }\n const segment = pattern.slice(pseudoStart, i);\n result += segment;\n lastCh = segment[segment.length - 1] || lastCh;\n continue;\n }\n\n if (/[a-z]/.test(char)) {\n const tagStart = i;\n while (i < pattern.length && /[a-z0-9-]/.test(pattern[i])) {\n i++;\n }\n if (result && lastCh !== ' ') {\n result += ' ';\n }\n const segment = pattern.slice(tagStart, i);\n result += segment;\n lastCh = segment[segment.length - 1] || lastCh;\n continue;\n }\n\n if (char === '.') {\n const attachToLast =\n lastCh === ']' || lastCh === '@' || /[a-zA-Z0-9-]/.test(lastCh);\n if (result && !attachToLast && lastCh !== ' ') {\n result += ' ';\n }\n const clsStart = i;\n i++;\n while (i < pattern.length && /[a-zA-Z0-9_-]/.test(pattern[i])) {\n i++;\n }\n const segment = pattern.slice(clsStart, i);\n result += segment;\n lastCh = segment[segment.length - 1] || lastCh;\n continue;\n }\n\n if (char === '[') {\n const attachToLast =\n lastCh === ']' || lastCh === '@' || /[a-zA-Z0-9-]/.test(lastCh);\n if (result && !attachToLast && lastCh !== ' ') {\n result += ' ';\n }\n const attrStart = i;\n let depth = 0;\n while (i < pattern.length) {\n if (pattern[i] === '[') depth++;\n if (pattern[i] === ']') depth--;\n i++;\n if (depth === 0) break;\n }\n result += pattern.slice(attrStart, i);\n lastCh = ']';\n continue;\n }\n\n result += char;\n lastCh = char;\n i++;\n }\n\n return result;\n}\n\n/**\n * Determine if the sub-element key should be auto-injected based on pattern ending.\n *\n * Key injection rules (when no @ placeholder is present):\n *\n * | Pattern Ending | Inject Key? | Example | Result |\n * |----------------|-------------|---------|--------|\n * | Combinator (>, +, ~) | Yes | `'>Body>'` | `> [data-element=\"Body\"] > [el]` |\n * | Uppercase element | Yes | `'>Body>Row'` | `> [el1] > [el2] [key]` |\n * | Lowercase tag | No | `'h1'` | ` h1` |\n * | Universal (*) | No | `'h1 *'` | ` h1 *` |\n * | Pseudo (:hover, ::before) | No | `'::before'` | `::before` |\n * | Class (.active) | No | `'.active'` | `.active` |\n * | Attribute ([type]) | No | `'[type=\"text\"]'` | `[type=\"text\"]` |\n *\n * @param pattern - The normalized pattern (after stripping &)\n * @returns true if key should be injected, false otherwise\n *\n * @example\n * shouldInjectKey('>') // → true (trailing combinator)\n * shouldInjectKey('>Body>Row') // → true (ends with element)\n * shouldInjectKey('h1') // → false (ends with tag)\n * shouldInjectKey('*') // → false (universal selector)\n * shouldInjectKey('::before') // → false (ends with pseudo)\n * shouldInjectKey('.active') // → false (ends with class)\n * shouldInjectKey('a:hover') // → false (ends with pseudo)\n * shouldInjectKey('button.primary') // → false (ends with class)\n */\nfunction shouldInjectKey(pattern: string): boolean {\n const trimmed = pattern.trim();\n\n // Rule 1: Ends with combinator → inject key after it\n // e.g., '>' → '> [data-element=\"Key\"]'\n if (/[>+~]$/.test(trimmed)) {\n return true;\n }\n\n // Rule 2: Ends with uppercase element name → inject key as descendant\n // The lookbehind ensures we're matching a standalone element name, not\n // part of a class like .myClass (where C is preceded by lowercase)\n // e.g., '>Body' → '> [data-element=\"Body\"] [data-element=\"Key\"]'\n if (/(?:^|[\\s>+~\\]:])[A-Z][a-zA-Z0-9]*$/.test(trimmed)) {\n return true;\n }\n\n // Otherwise (tags, universal *, pseudo, class, attribute) → no injection\n // The pattern is complete as-is, applying to root or a specific selector\n return false;\n}\n\n/**\n * Normalize selector suffix from $ property\n */\nfunction normalizeSelectorSuffix(suffix: string): string {\n if (!suffix) return '';\n return suffix.startsWith('&') ? suffix.slice(1) : suffix;\n}\n\n/**\n * Build handler queue from style keys\n */\nfunction buildHandlerQueue(\n styleKeys: string[],\n styles: Styles,\n): { handler: StyleHandler; styleMap: StyleMap }[] {\n const queue: { handler: StyleHandler; styleMap: StyleMap }[] = [];\n const seenHandlers = new Set<StyleHandler>();\n\n for (const styleName of styleKeys) {\n let handlers: StyleHandler[] = STYLE_HANDLER_MAP[styleName];\n\n if (!handlers) {\n handlers = STYLE_HANDLER_MAP[styleName] = [createStyle(styleName)];\n }\n\n for (const handler of handlers) {\n if (seenHandlers.has(handler)) continue;\n seenHandlers.add(handler);\n\n const lookupStyles = handler.__lookupStyles;\n const styleMap: StyleMap = {};\n\n for (const name of lookupStyles) {\n const val = styles[name];\n if (val !== undefined) {\n styleMap[name] = val as StyleValue | StyleValueStateMap;\n }\n }\n\n queue.push({ handler, styleMap });\n }\n }\n\n return queue;\n}\n\n/**\n * Compute all valid state combinations for a handler's lookup styles\n */\nfunction computeStateCombinations(\n exclusiveByStyle: Map<string, ExclusiveStyleEntry[]>,\n lookupStyles: string[],\n): { condition: ConditionNode; values: Record<string, StyleValue> }[] {\n // Get entries for each style\n const entriesPerStyle = lookupStyles.map(\n (style) => exclusiveByStyle.get(style) || [],\n );\n\n // Cartesian product of all combinations\n const combinations = cartesianProduct(entriesPerStyle);\n\n // Build snapshots, simplifying and filtering impossible combinations\n const snapshots: {\n condition: ConditionNode;\n values: Record<string, StyleValue>;\n }[] = [];\n\n for (const combo of combinations) {\n // Combine all exclusive conditions with AND\n const conditions = combo.map((e) => e.exclusiveCondition);\n const combined = and(...conditions);\n const simplified = simplifyCondition(combined);\n\n // Skip impossible combinations\n if (simplified.kind === 'false') continue;\n\n // Build values map\n const values: Record<string, StyleValue> = {};\n for (const entry of combo) {\n values[entry.styleKey] = entry.value;\n }\n\n snapshots.push({\n condition: simplified,\n values,\n });\n }\n\n return snapshots;\n}\n\n/**\n * Cartesian product of arrays\n */\nfunction cartesianProduct<T>(arrays: T[][]): T[][] {\n if (arrays.length === 0) return [[]];\n\n const nonEmpty = arrays.filter((a) => a.length > 0);\n if (nonEmpty.length === 0) return [[]];\n\n let result: T[][] = [[]];\n for (const arr of nonEmpty) {\n const next: T[][] = [];\n for (const combo of result) {\n for (const item of arr) {\n const newCombo = new Array<T>(combo.length + 1);\n for (let i = 0; i < combo.length; i++) newCombo[i] = combo[i];\n newCombo[combo.length] = item;\n next.push(newCombo);\n }\n }\n result = next;\n }\n return result;\n}\n\nconst declStringCache = new WeakMap<Record<string, string>, string>();\n\nfunction stringifyDeclarations(decl: Record<string, string>): string {\n let cached = declStringCache.get(decl);\n if (cached === undefined) {\n cached = JSON.stringify(decl);\n declStringCache.set(decl, cached);\n }\n return cached;\n}\n\n/**\n * Merge rules with identical CSS output\n */\nfunction mergeByValue(rules: ComputedRule[]): ComputedRule[] {\n const groups = new Map<string, ComputedRule[]>();\n\n for (const rule of rules) {\n const key = `${rule.selectorSuffix}|${stringifyDeclarations(rule.declarations)}`;\n if (!groups.has(key)) {\n groups.set(key, []);\n }\n groups.get(key)!.push(rule);\n }\n\n // Merge conditions with OR for each group\n const merged: ComputedRule[] = [];\n\n for (const [, groupRules] of groups) {\n if (groupRules.length === 1) {\n merged.push(groupRules[0]);\n } else {\n // Merge conditions with OR\n const mergedCondition = simplifyCondition(\n or(...groupRules.map((r) => r.condition)),\n );\n merged.push({\n condition: mergedCondition,\n declarations: groupRules[0].declarations,\n selectorSuffix: groupRules[0].selectorSuffix,\n });\n }\n }\n\n return merged;\n}\n\n/**\n * Build selector fragment from a variant (without className prefix)\n */\nfunction buildSelectorFromVariant(\n variant: SelectorVariant,\n selectorSuffix: string,\n): string {\n let selector = '';\n\n // Add flat modifier + pseudo selectors (sorted for canonical output)\n selector += branchToCSS([\n ...variant.modifierConditions,\n ...variant.pseudoConditions,\n ]);\n\n // Add selector groups (:is()/:not() on element)\n for (const group of variant.selectorGroups) {\n selector += selectorGroupToCSS(group);\n }\n\n // Add parent selectors (before sub-element suffix)\n if (variant.parentGroups.length > 0) {\n selector += parentGroupsToCSS(variant.parentGroups);\n }\n\n selector += selectorSuffix;\n\n // Add own groups (:is()/:not() on sub-element)\n const ownOptimized = optimizeGroups(variant.ownGroups);\n for (const group of ownOptimized) {\n selector += selectorGroupToCSS(group);\n }\n\n return selector;\n}\n\n/**\n * Materialize a computed rule to final CSS format\n *\n * Returns an array because OR conditions may generate multiple CSS rules\n * (when different branches have different at-rules)\n */\nfunction materializeComputedRule(rule: ComputedRule): CSSRule[] {\n const components = conditionToCSS(rule.condition);\n\n if (components.isImpossible || components.variants.length === 0) {\n return [];\n }\n\n const declarations = Object.entries(rule.declarations)\n .map(([prop, value]) => `${prop}: ${value};`)\n .join(' ');\n\n // Helper to get root prefix key for grouping\n const getRootPrefixKey = (variant: SelectorVariant): string => {\n return rootGroupsToCSS(variant.rootGroups) || '';\n };\n\n // Group variants by their at-rules + startingStyle (variants with same context can be combined with commas)\n const byAtRules = new Map<\n string,\n {\n variants: SelectorVariant[];\n atRules: string[];\n rootPrefix?: string;\n startingStyle?: boolean;\n }\n >();\n\n for (const variant of components.variants) {\n const atRules = buildAtRulesFromVariant(variant);\n const startingStyle = variant.startingStyle;\n const key =\n atRules.sort().join('|||') +\n '###' +\n getRootPrefixKey(variant) +\n '###' +\n (startingStyle ? '1' : '0');\n\n const group = byAtRules.get(key);\n if (group) {\n group.variants.push(variant);\n } else {\n byAtRules.set(key, {\n variants: [variant],\n atRules,\n rootPrefix: rootGroupsToCSS(variant.rootGroups),\n startingStyle: startingStyle || undefined,\n });\n }\n }\n\n // Generate one CSSRule per at-rules group\n const rules: CSSRule[] = [];\n for (const [, group] of byAtRules) {\n // Merge variants that differ only in flat modifier/pseudo conditions\n // into :is() groups before building selector strings\n const mergedVariants = mergeVariantsIntoSelectorGroups(group.variants);\n\n // Build selector fragments for each variant (will be joined with className later)\n const selectorFragments = mergedVariants.map((v) =>\n buildSelectorFromVariant(v, rule.selectorSuffix),\n );\n\n // Store as array if multiple, string if single\n const selector =\n selectorFragments.length === 1 ? selectorFragments[0] : selectorFragments;\n\n const cssRule: CSSRule = {\n selector,\n declarations,\n };\n\n if (group.atRules.length > 0) {\n cssRule.atRules = group.atRules;\n }\n\n if (group.rootPrefix) {\n cssRule.rootPrefix = group.rootPrefix;\n }\n\n if (group.startingStyle) {\n cssRule.startingStyle = true;\n }\n\n rules.push(cssRule);\n }\n\n return rules;\n}\n\n// ============================================================================\n// StyleResult merging (group by selector + at-rules)\n// ============================================================================\n\n/**\n * Merge StyleResult entries that share the same selector and at-rules,\n * concatenating their declarations into a single rule.\n *\n * This reduces CSS output size when many style keys (e.g. design tokens)\n * resolve to the same selector/state combination.\n */\nfunction mergeStyleResults(results: StyleResult[]): StyleResult[] {\n if (results.length <= 1) return results;\n\n const groups = new Map<string, StyleResult>();\n\n for (const result of results) {\n const atKey = result.atRules?.join('|') ?? '';\n const key = `${atKey}||${result.selector}||${result.startingStyle ? '1' : '0'}`;\n\n const existing = groups.get(key);\n if (existing) {\n existing.declarations = existing.declarations\n ? `${existing.declarations} ${result.declarations}`\n : result.declarations;\n } else {\n groups.set(key, { ...result });\n }\n }\n\n return Array.from(groups.values());\n}\n\n// ============================================================================\n// Public API: renderStyles (compatible with old API)\n// ============================================================================\n\n/**\n * Options for renderStyles when using direct selector mode.\n */\nexport interface RenderStylesOptions {\n /**\n * Whether to double the class selector for increased specificity.\n * When true, `.myClass` becomes `.myClass.myClass` for higher specificity.\n *\n * @default false - User-provided selectors are not doubled.\n *\n * Note: This only applies when a classNameOrSelector is provided.\n * When renderStyles returns RenderResult with needsClassName=true,\n * the injector handles doubling automatically.\n */\n doubleSelector?: boolean;\n}\n\n/**\n * Render styles to CSS rules.\n *\n * When called without classNameOrSelector, returns RenderResult with needsClassName=true.\n * When called with a selector/className string, returns StyleResult[] for direct injection.\n */\nexport function renderStyles(\n styles?: Styles,\n classNameOrSelector?: undefined,\n options?: undefined,\n pipelineCacheKey?: string,\n): RenderResult;\nexport function renderStyles(\n styles: Styles | undefined,\n classNameOrSelector: string,\n options?: RenderStylesOptions,\n): StyleResult[];\nexport function renderStyles(\n styles?: Styles,\n classNameOrSelector?: string,\n options?: RenderStylesOptions,\n pipelineCacheKey?: string,\n): RenderResult | StyleResult[] {\n // Check if we have a direct selector/className\n const directSelector = !!classNameOrSelector;\n\n // Check cache first when a pre-computed key is available.\n // This allows callers to skip building the styles object on cache hit.\n let rules: CSSRule[] | undefined;\n if (pipelineCacheKey) {\n rules = pipelineCache.get(pipelineCacheKey);\n }\n\n if (!rules && !styles) {\n return directSelector ? [] : { rules: [] };\n }\n\n // Use pre-computed cache key when available (from chunk path),\n // falling back to stringifyStyles for direct renderStyles() calls\n const cacheKey = pipelineCacheKey || stringifyStyles(styles!);\n if (!rules) {\n rules = pipelineCache.get(cacheKey);\n }\n\n if (!rules) {\n // styles is guaranteed non-null here: early return above handles (!rules && !styles)\n const parserContext = createStateParserContext(styles!);\n rules = runPipeline(styles!, parserContext);\n pipelineCache.set(cacheKey, rules);\n }\n\n // Direct selector/className mode: return StyleResult[] directly\n if (directSelector) {\n const shouldDouble = options?.doubleSelector ?? false;\n\n const results = rules.map((rule): StyleResult => {\n // Handle selector as array (OR conditions) or string\n const selectorParts = Array.isArray(rule.selector)\n ? rule.selector\n : rule.selector\n ? [rule.selector]\n : [''];\n\n const finalSelector = selectorParts\n .map((part) => {\n let sel = part\n ? `${classNameOrSelector}${part}`\n : classNameOrSelector;\n\n // Double class selector for increased specificity if requested\n // This is used when the caller explicitly wants higher specificity\n if (shouldDouble && sel.startsWith('.')) {\n const classMatch = sel.match(/^\\.[a-zA-Z_-][a-zA-Z0-9_-]*/);\n if (classMatch) {\n const baseClass = classMatch[0];\n sel = baseClass + sel;\n }\n }\n\n // Handle root prefix for this selector\n if (rule.rootPrefix) {\n sel = `${rule.rootPrefix} ${sel}`;\n }\n\n return sel;\n })\n .join(', ');\n\n const result: StyleResult = {\n selector: finalSelector,\n declarations: rule.declarations,\n };\n\n if (rule.atRules && rule.atRules.length > 0) {\n result.atRules = rule.atRules;\n }\n\n if (rule.startingStyle) {\n result.startingStyle = true;\n }\n\n return result;\n });\n\n return mergeStyleResults(results);\n }\n\n // No className mode: return RenderResult with needsClassName flag\n // Normalize selector to string (join array with placeholder that injector will handle)\n return {\n rules: rules.map(\n (r): StyleResult => ({\n selector: Array.isArray(r.selector)\n ? r.selector.join('|||')\n : r.selector,\n declarations: r.declarations,\n atRules: r.atRules,\n needsClassName: true,\n rootPrefix: r.rootPrefix,\n startingStyle: r.startingStyle,\n }),\n ),\n };\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport type { ConditionNode } from './conditions';\nexport { and, or, not, trueCondition, falseCondition } from './conditions';\nexport { parseStateKey } from './parseStateKey';\nexport { simplifyCondition } from './simplify';\nexport { buildExclusiveConditions } from './exclusive';\nexport { conditionToCSS } from './materialize';\nexport type { CSSRule } from './materialize';\nexport { setWarningHandler, emitWarning } from './warnings';\nexport type {\n TastyWarning,\n TastyWarningCode,\n TastyWarningHandler,\n} from './warnings';\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/pipeline/index.ts"],"sourcesContent":["/**\n * Tasty Style Rendering Pipeline\n *\n * Main entrypoint for the style rendering pipeline. Transforms a `Styles`\n * object into an array of `CSSRule` objects ready for DOM injection.\n *\n * Per-handler stages (see docs/pipeline.md for full detail):\n * 0. PRE-PARSE NORMALIZATION - extractCompoundStates (exclusive.ts)\n * 1. PARSE CONDITIONS - parseStyleEntries + parseStateKey\n * 1b. MERGE ENTRIES BY VALUE - mergeEntriesByValue (exclusive.ts)\n * 2a. EXPAND USER OR BRANCHES - expandOrConditions (exclusive.ts)\n * 2b. BUILD EXCLUSIVE CONDITIONS - buildExclusiveConditions\n * 3. EXPAND DE MORGAN ORs - expandExclusiveOrs (exclusive.ts)\n * 4. COMPUTE STATE COMBINATIONS - computeStateCombinations\n * 5. CALL HANDLERS - run style handlers for each snapshot\n * 6. MERGE BY VALUE - mergeByValue (index.ts)\n * 7. MATERIALIZE CSS - conditionToCSS + materializeComputedRule\n *\n * Simplification (`simplifyCondition`) runs inside most stages; calls are\n * memoized by condition unique-id. The post-pass in `runPipeline` dedupes\n * identical rules and emits all `@starting-style` rules last so they win\n * the cascade over their equal-specificity normal counterparts.\n */\n\nimport { Lru } from '../parser/lru';\nimport type { StateParserContext } from '../states';\nimport {\n createStateParserContext,\n extractLocalPredefinedStates,\n} from '../states';\nimport { createStyle, STYLE_HANDLER_MAP } from '../styles';\nimport type { Styles } from '../styles/types';\nimport type {\n StyleHandler,\n StyleMap,\n StyleValue,\n StyleValueStateMap,\n} from '../utils/styles';\nimport { stringifyStyles } from '../utils/styles';\n\nimport type { ConditionNode } from './conditions';\nimport { and, or, trueCondition } from './conditions';\nimport type { ExclusiveStyleEntry } from './exclusive';\nimport {\n buildExclusiveConditions,\n expandExclusiveOrs,\n expandOrConditions,\n extractCompoundStates,\n isValueMapping,\n mergeEntriesByValue,\n parseStyleEntries,\n} from './exclusive';\nimport type { CSSRule, SelectorVariant } from './materialize';\nimport {\n branchToCSS,\n buildAtRulesFromVariant,\n conditionToCSS,\n mergeVariantsIntoSelectorGroups,\n optimizeGroups,\n parentGroupsToCSS,\n rootGroupsToCSS,\n selectorGroupToCSS,\n} from './materialize';\nimport { parseStateKey } from './parseStateKey';\nimport { simplifyCondition } from './simplify';\nimport { emitWarning } from './warnings';\n\n// ============================================================================\n// Types (compatible with old renderStyles API)\n// ============================================================================\n\n/**\n * Matches the old StyleResult interface for backward compatibility\n */\nexport interface StyleResult {\n selector: string;\n declarations: string;\n atRules?: string[];\n needsClassName?: boolean;\n rootPrefix?: string;\n /** When true, declarations are wrapped in @starting-style { ... } inside the selector rule */\n startingStyle?: boolean;\n}\n\n/**\n * Matches the old RenderResult interface for backward compatibility\n */\nexport interface RenderResult {\n rules: StyleResult[];\n className?: string;\n}\n\nexport interface PipelineResult {\n rules: CSSRule[];\n className?: string;\n}\n\ninterface ComputedRule {\n condition: ConditionNode;\n declarations: Record<string, string>;\n selectorSuffix: string;\n}\n\n// ============================================================================\n// Caching\n// ============================================================================\n\nconst pipelineCache = new Lru<string, CSSRule[]>(5000);\n\n// ============================================================================\n// Main Pipeline Function\n// ============================================================================\n\n/**\n * Render styles using the new pipeline.\n *\n * This is the main entrypoint that implements the complete flow.\n */\nexport function renderStylesPipeline(\n styles?: Styles,\n className?: string,\n pipelineCacheKey?: string,\n): PipelineResult {\n if (!styles) {\n return { rules: [], className };\n }\n\n // Use pre-computed cache key when available, falling back to stringifyStyles\n const cacheKey = pipelineCacheKey || stringifyStyles(styles);\n let rules = pipelineCache.get(cacheKey);\n\n if (!rules) {\n // Create parser context\n const parserContext = createStateParserContext(styles);\n\n // Run pipeline\n rules = runPipeline(styles, parserContext);\n\n // Cache result\n pipelineCache.set(cacheKey, rules);\n }\n\n // If no className, rules need it to be prepended later\n if (!className) {\n return {\n rules: rules.map((r) => ({\n ...r,\n needsClassName: true,\n })),\n };\n }\n\n // Prepend className to selectors\n const finalRules = rules.map((rule) => {\n // Parse the selector to find where to insert className\n let selector = rule.selector;\n\n // If selector starts with :root, insert className after the :root part\n if (rule.rootPrefix) {\n selector = `${rule.rootPrefix} .${className}.${className}${selector}`;\n } else {\n selector = `.${className}.${className}${selector}`;\n }\n\n return {\n ...rule,\n selector,\n };\n });\n\n return {\n rules: finalRules,\n className,\n };\n}\n\n/**\n * Check if a cache key exists in the pipeline cache.\n * Used by renderStylesForChunk to avoid building filtered styles on cache hit.\n */\nexport function hasPipelineCacheEntry(cacheKey: string): boolean {\n return pipelineCache.get(cacheKey) !== undefined;\n}\n\n/**\n * Clear the pipeline cache (for testing)\n */\nexport function clearPipelineCache(): void {\n pipelineCache.clear();\n}\n\n// ============================================================================\n// Pipeline Implementation\n// ============================================================================\n\nfunction runPipeline(\n styles: Styles,\n parserContext: StateParserContext,\n): CSSRule[] {\n const allRules: CSSRule[] = [];\n\n // Process styles recursively (including nested selectors)\n processStyles(styles, '', parserContext, allRules);\n\n // Deduplicate rules\n const seen = new Set<string>();\n const dedupedRules = allRules.filter((rule) => {\n const key = `${rule.selector}|${rule.declarations}|${rule.atRules?.join('|') ?? ''}|${rule.rootPrefix || ''}|${rule.startingStyle ? '1' : '0'}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n\n // @starting-style rules must come AFTER normal rules for the same selector.\n // They share the same specificity, so source order decides the cascade.\n // If a @starting-style rule appears before its normal counterpart,\n // the later normal rule overrides the starting value.\n const normal: CSSRule[] = [];\n const starting: CSSRule[] = [];\n\n for (const rule of dedupedRules) {\n if (rule.startingStyle) {\n starting.push(rule);\n } else {\n normal.push(rule);\n }\n }\n\n return normal.concat(starting);\n}\n\n/**\n * Process styles at a given nesting level.\n *\n * Splits keys into nested-selector keys and style-handler keys, recurses\n * into nested selectors, then runs the per-handler stages 1–7 over the\n * style keys.\n */\nfunction processStyles(\n styles: Styles,\n selectorSuffix: string,\n parserContext: StateParserContext,\n allRules: CSSRule[],\n): void {\n const keys = Object.keys(styles);\n\n // Separate selector keys from style keys.\n // Skip @keyframes (processed separately) and other @-prefixed keys\n // (predefined states), which are not handler entries.\n const selectorKeys = keys.filter((key) => isSelector(key));\n const styleKeys = keys.filter(\n (key) => !isSelector(key) && !key.startsWith('@'),\n );\n\n // Process nested selectors first\n processNestedSelectors(\n styles,\n selectorKeys,\n selectorSuffix,\n parserContext,\n allRules,\n );\n\n // Process the handler queue for this level's style keys\n const handlerQueue = buildHandlerQueue(styleKeys, styles);\n processHandlerQueue(handlerQueue, selectorSuffix, parserContext, allRules);\n}\n\n/**\n * Recurse into nested selector keys. Each nested key may expand into multiple\n * suffixes (comma-separated patterns); each suffix is processed independently\n * with the parent's parser context augmented for sub-element scope.\n */\nfunction processNestedSelectors(\n styles: Styles,\n selectorKeys: string[],\n selectorSuffix: string,\n parserContext: StateParserContext,\n allRules: CSSRule[],\n): void {\n for (const key of selectorKeys) {\n const nestedStyles = styles[key] as Styles;\n if (!nestedStyles || typeof nestedStyles !== 'object') continue;\n\n // Get all selectors (handles comma-separated patterns)\n const suffixes = getAllSelectors(key, nestedStyles);\n if (!suffixes) continue; // Invalid selector, skip\n\n // Remove $ from nested styles\n const { $: _$, ...cleanedStyles } = nestedStyles;\n\n // Extract local predefined states scoped to this sub-element\n const subLocalStates = extractLocalPredefinedStates(cleanedStyles);\n const hasSubStates = Object.keys(subLocalStates).length > 0;\n const subContext: StateParserContext = {\n ...parserContext,\n isSubElement: true,\n localPredefinedStates: hasSubStates\n ? { ...parserContext.localPredefinedStates, ...subLocalStates }\n : parserContext.localPredefinedStates,\n };\n\n // Process for each selector (multiple selectors = same styles applied to each)\n for (const suffix of suffixes) {\n processStyles(\n cleanedStyles,\n selectorSuffix + suffix,\n subContext,\n allRules,\n );\n }\n }\n}\n\n/**\n * Run the per-handler pipeline (stages 1–7) over a handler queue and append\n * the resulting CSS rules to `allRules`.\n */\nfunction processHandlerQueue(\n handlerQueue: ReturnType<typeof buildHandlerQueue>,\n selectorSuffix: string,\n parserContext: StateParserContext,\n allRules: CSSRule[],\n): void {\n for (const { handler, styleMap } of handlerQueue) {\n const lookupStyles = handler.__lookupStyles;\n\n // Stages 0–3: build exclusive conditions for each style this handler\n // depends on (extractCompoundStates → parse → mergeEntriesByValue →\n // expandOrConditions → buildExclusiveConditions → expandExclusiveOrs).\n const exclusiveByStyle = buildExclusivesForHandler(\n lookupStyles,\n styleMap,\n parserContext,\n );\n\n // Stage 4: Compute all valid state combinations\n const stateSnapshots = computeStateCombinations(\n exclusiveByStyle,\n lookupStyles,\n );\n\n // Stage 5: Call handler for each snapshot\n const computedRules = invokeHandler(\n handler,\n stateSnapshots,\n selectorSuffix,\n );\n\n // Stage 6: Merge rules with identical CSS output\n const mergedRules = mergeByValue(computedRules);\n\n // Stage 7: Materialize to CSS\n for (const rule of mergedRules) {\n const cssRules = materializeComputedRule(rule);\n allRules.push(...cssRules);\n }\n }\n}\n\n/**\n * Stages 0–3 for a single handler: take the handler's looked-up style names,\n * resolve each style's value map into a list of mutually-exclusive entries.\n * Simple non-mapping values produce a single TRUE-conditioned entry.\n */\nfunction buildExclusivesForHandler(\n lookupStyles: readonly string[],\n styleMap: StyleMap,\n parserContext: StateParserContext,\n): Map<string, ExclusiveStyleEntry[]> {\n const exclusiveByStyle = new Map<string, ExclusiveStyleEntry[]>();\n\n for (const styleName of lookupStyles) {\n const value = styleMap[styleName];\n if (value === undefined) continue;\n\n if (isValueMapping(value)) {\n // Stage 0: Eliminate redundant compound state dimensions before parsing.\n // E.g. { '': A, '@dark': B, '@hc': A, '@dark & @hc': B }\n // reduces to { '': A, '@dark': B } because @hc is irrelevant.\n const reduced = extractCompoundStates(\n value as Record<string, StyleValue>,\n );\n\n // Stage 1: Parse entries from value mapping.\n const parsed = parseStyleEntries(styleName, reduced, (stateKey) =>\n parseStateKey(stateKey, { context: parserContext }),\n );\n\n // Stage 1b: Merge entries that share the same value before exclusive\n // expansion. Prevents combinatorial blowup when e.g. @dark and\n // @dark & @high-contrast map to the same color.\n const merged = mergeEntriesByValue(parsed);\n\n // Stage 2a: Expand user OR conditions into exclusive branches\n // (`A | B | C` becomes `A`, `B & !A`, `C & !A & !B`).\n const expanded = expandOrConditions(merged);\n\n // Stage 2b: Build exclusive conditions across all entries.\n const exclusive = buildExclusiveConditions(expanded);\n\n // Stage 3: Expand De Morgan ORs from negation into at-rule-aware\n // exclusive branches. `!A | !B` → `!A`, `A & !B`. Each branch keeps\n // the correct at-rule context.\n const fullyExpanded = expandExclusiveOrs(exclusive);\n exclusiveByStyle.set(styleName, fullyExpanded);\n } else {\n // Simple value — single entry with TRUE condition.\n exclusiveByStyle.set(styleName, [\n {\n styleKey: styleName,\n stateKey: '',\n value,\n condition: trueCondition(),\n priority: 0,\n exclusiveCondition: trueCondition(),\n },\n ]);\n }\n }\n\n return exclusiveByStyle;\n}\n\n/**\n * Stage 5: invoke the handler for each state snapshot and translate its\n * return value into ComputedRule entries (one per declaration set, fanned\n * out across any `$` selector suffixes the handler returns).\n */\nfunction invokeHandler(\n handler: StyleHandler,\n stateSnapshots: ReturnType<typeof computeStateCombinations>,\n selectorSuffix: string,\n): ComputedRule[] {\n const computedRules: ComputedRule[] = [];\n\n for (const snapshot of stateSnapshots) {\n const result = handler(snapshot.values as StyleValueStateMap);\n if (!result) continue;\n\n // Handler may return single or array\n const results = Array.isArray(result) ? result : [result];\n\n for (const r of results) {\n if (!r || typeof r !== 'object') continue;\n\n const { $, ...styleProps } = r;\n const declarations: Record<string, string> = {};\n\n for (const [prop, val] of Object.entries(styleProps)) {\n if (val != null && val !== '') {\n declarations[prop] = String(val);\n }\n }\n\n if (Object.keys(declarations).length === 0) continue;\n\n // Handle $ suffixes\n const suffixes = $\n ? (Array.isArray($) ? $ : [$]).map(\n (s) => selectorSuffix + normalizeSelectorSuffix(String(s)),\n )\n : [selectorSuffix];\n\n for (const suffix of suffixes) {\n computedRules.push({\n condition: snapshot.condition,\n declarations,\n selectorSuffix: suffix,\n });\n }\n }\n }\n\n return computedRules;\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Check if a key is a CSS selector\n */\nexport function isSelector(key: string): boolean {\n return key.startsWith('&') || key.startsWith('.') || /^[A-Z]/.test(key);\n}\n\n/**\n * Result of processing a selector affix ($) pattern.\n *\n * @example\n * // Valid result with multiple selectors\n * { valid: true, selectors: ['> [data-element=\"Cell\"]', ' [data-element=\"Body\"] > [data-element=\"Cell\"]'] }\n *\n * // Invalid result with error message\n * { valid: false, reason: 'Selector affix \"+\" targets elements outside the root scope.' }\n */\ntype AffixResult =\n | { valid: true; selectors: string[] }\n | { valid: false; reason: string };\n\n/**\n * Get all selector suffixes for a sub-element key.\n *\n * Handles three types of selector keys:\n * - `&` prefix: Raw selector suffix (e.g., `&:hover` → `:hover`)\n * - `.` prefix: Class selector (e.g., `.active` → ` .active`)\n * - Uppercase: Sub-element with optional `$` affix pattern\n *\n * @param key - The sub-element key (e.g., 'Label', '&:hover', '.active')\n * @param styles - The styles object, may contain `$` property for selector affix\n * @returns Array of selector suffixes, or null if invalid (with console warning)\n *\n * @example\n * getAllSelectors('Label', {})\n * // → [' [data-element=\"Label\"]']\n *\n * getAllSelectors('Cell', { $: '>, >Body>' })\n * // → ['> [data-element=\"Cell\"]', ' [data-element=\"Body\"] > [data-element=\"Cell\"]']\n */\nfunction getAllSelectors(key: string, styles?: Styles): string[] | null {\n if (key.startsWith('&')) {\n return [key.slice(1)];\n }\n\n if (key.startsWith('.')) {\n return [` ${key}`];\n }\n\n if (/^[A-Z]/.test(key)) {\n const affix = styles?.$;\n if (affix !== undefined) {\n const result = processAffix(String(affix), key);\n if (!result.valid) {\n emitWarning('INVALID_SELECTOR_AFFIX', result.reason);\n return null;\n }\n return result.selectors;\n }\n return [` [data-element=\"${key}\"]`];\n }\n\n return null;\n}\n\n/**\n * Process selector affix pattern and return selector(s)\n *\n * Supports:\n * - Direct child: '>'\n * - Chained elements: '>Body>Row>'\n * - HTML tags (no key injection): 'h1', '>ul>li', 'button:hover'\n * - Universal selector: '*', 'h1 *'\n * - Pseudo-elements on root: '::before'\n * - Pseudo on sub-element: '@::before', '>@:hover'\n * - Classes: '.active', '>@.active'\n * - Multiple selectors: '>, >Body>'\n * - Sibling combinators (after element): '>Item+', '>Item~'\n */\nfunction processAffix(affix: string, key: string): AffixResult {\n const trimmed = affix.trim();\n\n // Empty = default behavior (descendant selector with key)\n if (!trimmed) {\n return { valid: true, selectors: [` [data-element=\"${key}\"]`] };\n }\n\n // Split by comma for multiple selectors\n const patterns = trimmed.split(',').map((p) => p.trim());\n const selectors: string[] = [];\n\n for (const pattern of patterns) {\n const validation = validatePattern(pattern);\n if (!validation.valid) {\n return validation;\n }\n\n const selector = processSinglePattern(pattern, key);\n selectors.push(selector);\n }\n\n return { valid: true, selectors };\n}\n\n/**\n * Recognized token patterns for selector affix validation.\n *\n * These patterns are used to tokenize and validate `$` affix strings.\n * Order matters: more specific patterns must come first to avoid\n * partial matches (e.g., `::before` must match before `:` alone).\n *\n * Unrecognized tokens (like `#id`, `*`, or numbers) will cause validation to fail.\n */\nconst VALID_TOKEN_PATTERNS = [\n /^[>+~]/, // Combinators: >, +, ~\n /^\\*/, // Universal selector (*)\n /^[A-Z][a-zA-Z0-9]*/, // Uppercase element names → [data-element=\"...\"]\n /^@/, // @ placeholder for key injection position\n /^::?[a-z][a-z0-9-]*(?:\\([^)]*\\))?/, // Pseudo-elements/classes (:hover, ::before, :not(.x))\n /^\\.[a-zA-Z_-][a-zA-Z0-9_-]*/, // Class selectors (.active, .is-open)\n /^\\[[^\\]]+\\]/, // Attribute selectors ([type=\"text\"], [role])\n /^[a-z][a-z0-9-]*/, // HTML tag names (a, div, button, my-component)\n /^\\s+/, // Whitespace (ignored during parsing)\n /^&/, // Root reference (stripped, kept for backward compat)\n];\n\n/**\n * Scan a pattern for unrecognized tokens.\n *\n * Iterates through the pattern, consuming recognized tokens until\n * either the pattern is fully consumed (valid) or an unrecognized\n * character sequence is found (invalid).\n *\n * @param pattern - The selector pattern to validate\n * @returns The first unrecognized token found, or null if all tokens are valid\n *\n * @example\n * findUnrecognizedTokens('>Body>Row>') // → null (valid)\n * findUnrecognizedTokens('123') // → '123' (invalid)\n * findUnrecognizedTokens('#myId') // → '#' (invalid)\n */\nfunction findUnrecognizedTokens(pattern: string): string | null {\n let remaining = pattern;\n\n while (remaining.length > 0) {\n let matched = false;\n\n for (const regex of VALID_TOKEN_PATTERNS) {\n const match = remaining.match(regex);\n if (match) {\n remaining = remaining.slice(match[0].length);\n matched = true;\n break;\n }\n }\n\n if (!matched) {\n // Found unrecognized content - extract the problematic part\n const unrecognized = remaining.match(/^[^\\s>+~@.:[\\]A-Z]+/);\n return unrecognized ? unrecognized[0] : remaining[0];\n }\n }\n\n return null;\n}\n\n/**\n * Validate a selector pattern for structural correctness.\n *\n * Checks for:\n * 1. Out-of-scope selectors: Patterns starting with `+` or `~` target siblings\n * of the root element, which is outside the component's DOM scope.\n * 2. Consecutive combinators: Patterns like `>>` or `>+` are malformed CSS.\n * 3. Unrecognized tokens: Characters/sequences not matching valid CSS selectors.\n *\n * @param pattern - A single selector pattern (already split by comma)\n * @returns AffixResult indicating validity and error reason if invalid\n *\n * @example\n * validatePattern('>Body>Row>') // → { valid: true, selectors: [] }\n * validatePattern('+') // → { valid: false, reason: '...outside root scope...' }\n * validatePattern('>>') // → { valid: false, reason: '...consecutive combinators...' }\n */\nfunction validatePattern(pattern: string): AffixResult {\n const trimmed = pattern.trim();\n\n // Patterns starting with + or ~ target siblings of the root element,\n // which is outside the component's scope. Valid sibling patterns must\n // be preceded by an element: \">Item+\", \">Item~\"\n if (/^[+~]/.test(trimmed)) {\n return {\n valid: false,\n reason:\n `Selector affix \"${pattern}\" targets elements outside the root scope. ` +\n `Sibling selectors (+, ~) must be preceded by an element inside the root. ` +\n `Use \">Element+\" or \">Element~\" instead.`,\n };\n }\n\n // Check for consecutive combinators\n if (/[>+~]{2,}/.test(trimmed.replace(/\\s+/g, ''))) {\n return {\n valid: false,\n reason: `Selector affix \"${pattern}\" contains consecutive combinators.`,\n };\n }\n\n // Check for unrecognized tokens (e.g., lowercase text like \"foo\")\n const unrecognized = findUnrecognizedTokens(trimmed);\n if (unrecognized) {\n return {\n valid: false,\n reason:\n `Selector affix \"${pattern}\" contains unrecognized token \"${unrecognized}\". ` +\n `Valid tokens: combinators (>, +, ~), element names (Uppercase), ` +\n `@ placeholder, pseudo (:hover, ::before), class (.name), attribute ([attr]).`,\n };\n }\n\n return { valid: true, selectors: [] };\n}\n\n/**\n * Process a single selector pattern into a CSS selector suffix.\n *\n * This is the main transformation function that converts a `$` affix pattern\n * into a valid CSS selector suffix. It handles:\n *\n * 1. `@` placeholder replacement with `[data-element=\"key\"]`\n * 2. Key injection based on pattern ending (see `shouldInjectKey`)\n * 3. Proper spacing for descendant vs direct child selectors\n *\n * @param pattern - A single validated selector pattern\n * @param key - The sub-element key to inject (e.g., 'Label', 'Cell')\n * @returns CSS selector suffix ready to append to the root selector\n *\n * @example\n * processSinglePattern('>', 'Row')\n * // → '> [data-element=\"Row\"]'\n *\n * processSinglePattern('>Body>Row>', 'Cell')\n * // → '> [data-element=\"Body\"] > [data-element=\"Row\"] > [data-element=\"Cell\"]'\n *\n * processSinglePattern('::before', 'Before')\n * // → '::before' (no key injection for pseudo on root)\n *\n * processSinglePattern('>@:hover', 'Item')\n * // → '> [data-element=\"Item\"]:hover'\n */\nfunction processSinglePattern(pattern: string, key: string): string {\n // Strip leading & if present (implicit root reference, kept for compat)\n const normalized = pattern.replace(/^&/, '').trim();\n\n if (!normalized) {\n return ` [data-element=\"${key}\"]`;\n }\n\n // Pseudo-elements/classes at start apply directly to root (no space prefix)\n const startsWithPseudo = /^::?[a-z]/.test(normalized);\n\n // Transform the pattern: convert element names and normalize spacing\n let result = transformPattern(normalized);\n\n // Handle @ placeholder: explicit key injection position\n if (result.includes('@')) {\n // Remove space between @ and following class/pseudo for proper attachment\n // e.g., \"@ .active\" → \"[el].active\", but \"@ > span\" → \"[el] > span\"\n result = result.replace(/@ (?=[.:])/g, '@');\n result = result.replace(/@/g, `[data-element=\"${key}\"]`);\n\n if (!startsWithPseudo && !result.startsWith(' ')) {\n result = ' ' + result;\n }\n return result;\n }\n\n // Auto-inject key based on pattern ending (see shouldInjectKey for rules)\n if (shouldInjectKey(normalized)) {\n result = result + ' ' + `[data-element=\"${key}\"]`;\n }\n\n // Add space prefix for selectors targeting inside root (not pseudo on root)\n if (!startsWithPseudo && !result.startsWith(' ')) {\n result = ' ' + result;\n }\n\n return result;\n}\n\n/**\n * Transform a selector pattern by converting element names and normalizing spacing.\n *\n * This is a character-by-character tokenizer that:\n * - Converts uppercase names to `[data-element=\"Name\"]` selectors\n * - Adds proper spacing around combinators (>, +, ~)\n * - Preserves lowercase tags, classes, pseudos, and attributes as-is\n * - Keeps @ placeholder for later replacement\n *\n * The tokenizer handles these token types in order:\n * 1. Whitespace (skipped)\n * 2. Combinators: >, +, ~ (add surrounding spaces)\n * 3. Universal selector: * (keep as-is with spacing)\n * 4. Uppercase names: Body, Row (convert to [data-element=\"...\"])\n * 5. @ placeholder (keep for later replacement)\n * 6. Pseudo: :hover, ::before (attach to previous token)\n * 7. Tags: a, div, button (keep as-is with spacing)\n * 8. Classes: .active (attach to previous element/tag/placeholder)\n * 9. Attributes: [type=\"text\"] (keep as-is)\n *\n * @param pattern - The raw selector pattern to transform\n * @returns Transformed pattern with proper CSS selector syntax\n *\n * @example\n * transformPattern('>Body>Row>')\n * // → '> [data-element=\"Body\"] > [data-element=\"Row\"] >'\n *\n * transformPattern('button.primary:hover')\n * // → 'button.primary:hover'\n */\nfunction transformPattern(pattern: string): string {\n let result = '';\n let lastCh = '';\n let i = 0;\n\n while (i < pattern.length) {\n const char = pattern[i];\n\n if (/\\s/.test(char)) {\n i++;\n continue;\n }\n\n if (/[>+~]/.test(char)) {\n if (result && lastCh !== ' ') {\n result += ' ';\n }\n result += char;\n lastCh = char;\n i++;\n continue;\n }\n\n if (char === '*') {\n if (result && lastCh !== ' ') {\n result += ' ';\n }\n result += '*';\n lastCh = '*';\n i++;\n continue;\n }\n\n if (/[A-Z]/.test(char)) {\n const nameStart = i;\n while (i < pattern.length && /[a-zA-Z0-9]/.test(pattern[i])) {\n i++;\n }\n if (result && lastCh !== ' ') {\n result += ' ';\n }\n const segment = `[data-element=\"${pattern.slice(nameStart, i)}\"]`;\n result += segment;\n lastCh = ']';\n continue;\n }\n\n if (char === '@') {\n if (result && lastCh !== ' ') {\n result += ' ';\n }\n result += '@';\n lastCh = '@';\n i++;\n continue;\n }\n\n if (char === ':') {\n const pseudoStart = i;\n while (\n i < pattern.length &&\n !/[\\s>+~,@]/.test(pattern[i]) &&\n !/[A-Z]/.test(pattern[i])\n ) {\n i++;\n }\n const segment = pattern.slice(pseudoStart, i);\n result += segment;\n lastCh = segment[segment.length - 1] || lastCh;\n continue;\n }\n\n if (/[a-z]/.test(char)) {\n const tagStart = i;\n while (i < pattern.length && /[a-z0-9-]/.test(pattern[i])) {\n i++;\n }\n if (result && lastCh !== ' ') {\n result += ' ';\n }\n const segment = pattern.slice(tagStart, i);\n result += segment;\n lastCh = segment[segment.length - 1] || lastCh;\n continue;\n }\n\n if (char === '.') {\n const attachToLast =\n lastCh === ']' || lastCh === '@' || /[a-zA-Z0-9-]/.test(lastCh);\n if (result && !attachToLast && lastCh !== ' ') {\n result += ' ';\n }\n const clsStart = i;\n i++;\n while (i < pattern.length && /[a-zA-Z0-9_-]/.test(pattern[i])) {\n i++;\n }\n const segment = pattern.slice(clsStart, i);\n result += segment;\n lastCh = segment[segment.length - 1] || lastCh;\n continue;\n }\n\n if (char === '[') {\n const attachToLast =\n lastCh === ']' || lastCh === '@' || /[a-zA-Z0-9-]/.test(lastCh);\n if (result && !attachToLast && lastCh !== ' ') {\n result += ' ';\n }\n const attrStart = i;\n let depth = 0;\n while (i < pattern.length) {\n if (pattern[i] === '[') depth++;\n if (pattern[i] === ']') depth--;\n i++;\n if (depth === 0) break;\n }\n result += pattern.slice(attrStart, i);\n lastCh = ']';\n continue;\n }\n\n result += char;\n lastCh = char;\n i++;\n }\n\n return result;\n}\n\n/**\n * Determine if the sub-element key should be auto-injected based on pattern ending.\n *\n * Key injection rules (when no @ placeholder is present):\n *\n * | Pattern Ending | Inject Key? | Example | Result |\n * |----------------|-------------|---------|--------|\n * | Combinator (>, +, ~) | Yes | `'>Body>'` | `> [data-element=\"Body\"] > [el]` |\n * | Uppercase element | Yes | `'>Body>Row'` | `> [el1] > [el2] [key]` |\n * | Lowercase tag | No | `'h1'` | ` h1` |\n * | Universal (*) | No | `'h1 *'` | ` h1 *` |\n * | Pseudo (:hover, ::before) | No | `'::before'` | `::before` |\n * | Class (.active) | No | `'.active'` | `.active` |\n * | Attribute ([type]) | No | `'[type=\"text\"]'` | `[type=\"text\"]` |\n *\n * @param pattern - The normalized pattern (after stripping &)\n * @returns true if key should be injected, false otherwise\n *\n * @example\n * shouldInjectKey('>') // → true (trailing combinator)\n * shouldInjectKey('>Body>Row') // → true (ends with element)\n * shouldInjectKey('h1') // → false (ends with tag)\n * shouldInjectKey('*') // → false (universal selector)\n * shouldInjectKey('::before') // → false (ends with pseudo)\n * shouldInjectKey('.active') // → false (ends with class)\n * shouldInjectKey('a:hover') // → false (ends with pseudo)\n * shouldInjectKey('button.primary') // → false (ends with class)\n */\nfunction shouldInjectKey(pattern: string): boolean {\n const trimmed = pattern.trim();\n\n // Rule 1: Ends with combinator → inject key after it\n // e.g., '>' → '> [data-element=\"Key\"]'\n if (/[>+~]$/.test(trimmed)) {\n return true;\n }\n\n // Rule 2: Ends with uppercase element name → inject key as descendant\n // The lookbehind ensures we're matching a standalone element name, not\n // part of a class like .myClass (where C is preceded by lowercase)\n // e.g., '>Body' → '> [data-element=\"Body\"] [data-element=\"Key\"]'\n if (/(?:^|[\\s>+~\\]:])[A-Z][a-zA-Z0-9]*$/.test(trimmed)) {\n return true;\n }\n\n // Otherwise (tags, universal *, pseudo, class, attribute) → no injection\n // The pattern is complete as-is, applying to root or a specific selector\n return false;\n}\n\n/**\n * Normalize selector suffix from $ property\n */\nfunction normalizeSelectorSuffix(suffix: string): string {\n if (!suffix) return '';\n return suffix.startsWith('&') ? suffix.slice(1) : suffix;\n}\n\n/**\n * Build handler queue from style keys\n */\nfunction buildHandlerQueue(\n styleKeys: string[],\n styles: Styles,\n): { handler: StyleHandler; styleMap: StyleMap }[] {\n const queue: { handler: StyleHandler; styleMap: StyleMap }[] = [];\n const seenHandlers = new Set<StyleHandler>();\n\n for (const styleName of styleKeys) {\n let handlers: StyleHandler[] = STYLE_HANDLER_MAP[styleName];\n\n if (!handlers) {\n handlers = STYLE_HANDLER_MAP[styleName] = [createStyle(styleName)];\n }\n\n for (const handler of handlers) {\n if (seenHandlers.has(handler)) continue;\n seenHandlers.add(handler);\n\n const lookupStyles = handler.__lookupStyles;\n const styleMap: StyleMap = {};\n\n for (const name of lookupStyles) {\n const val = styles[name];\n if (val !== undefined) {\n styleMap[name] = val as StyleValue | StyleValueStateMap;\n }\n }\n\n queue.push({ handler, styleMap });\n }\n }\n\n return queue;\n}\n\n/**\n * Compute all valid state combinations for a handler's lookup styles\n */\nfunction computeStateCombinations(\n exclusiveByStyle: Map<string, ExclusiveStyleEntry[]>,\n lookupStyles: string[],\n): { condition: ConditionNode; values: Record<string, StyleValue> }[] {\n // Get entries for each style\n const entriesPerStyle = lookupStyles.map(\n (style) => exclusiveByStyle.get(style) || [],\n );\n\n // Cartesian product of all combinations\n const combinations = cartesianProduct(entriesPerStyle);\n\n // Build snapshots, simplifying and filtering impossible combinations\n const snapshots: {\n condition: ConditionNode;\n values: Record<string, StyleValue>;\n }[] = [];\n\n for (const combo of combinations) {\n // Combine all exclusive conditions with AND\n const conditions = combo.map((e) => e.exclusiveCondition);\n const combined = and(...conditions);\n const simplified = simplifyCondition(combined);\n\n // Skip impossible combinations\n if (simplified.kind === 'false') continue;\n\n // Build values map\n const values: Record<string, StyleValue> = {};\n for (const entry of combo) {\n values[entry.styleKey] = entry.value;\n }\n\n snapshots.push({\n condition: simplified,\n values,\n });\n }\n\n return snapshots;\n}\n\n/**\n * Cartesian product of arrays\n */\nfunction cartesianProduct<T>(arrays: T[][]): T[][] {\n if (arrays.length === 0) return [[]];\n\n const nonEmpty = arrays.filter((a) => a.length > 0);\n if (nonEmpty.length === 0) return [[]];\n\n let result: T[][] = [[]];\n for (const arr of nonEmpty) {\n const next: T[][] = [];\n for (const combo of result) {\n for (const item of arr) {\n const newCombo = new Array<T>(combo.length + 1);\n for (let i = 0; i < combo.length; i++) newCombo[i] = combo[i];\n newCombo[combo.length] = item;\n next.push(newCombo);\n }\n }\n result = next;\n }\n return result;\n}\n\nconst declStringCache = new WeakMap<Record<string, string>, string>();\n\nfunction stringifyDeclarations(decl: Record<string, string>): string {\n let cached = declStringCache.get(decl);\n if (cached === undefined) {\n cached = JSON.stringify(decl);\n declStringCache.set(decl, cached);\n }\n return cached;\n}\n\n/**\n * Merge rules with identical CSS output\n */\nfunction mergeByValue(rules: ComputedRule[]): ComputedRule[] {\n const groups = new Map<string, ComputedRule[]>();\n\n for (const rule of rules) {\n const key = `${rule.selectorSuffix}|${stringifyDeclarations(rule.declarations)}`;\n if (!groups.has(key)) {\n groups.set(key, []);\n }\n groups.get(key)!.push(rule);\n }\n\n // Merge conditions with OR for each group\n const merged: ComputedRule[] = [];\n\n for (const [, groupRules] of groups) {\n if (groupRules.length === 1) {\n merged.push(groupRules[0]);\n } else {\n // Merge conditions with OR\n const mergedCondition = simplifyCondition(\n or(...groupRules.map((r) => r.condition)),\n );\n merged.push({\n condition: mergedCondition,\n declarations: groupRules[0].declarations,\n selectorSuffix: groupRules[0].selectorSuffix,\n });\n }\n }\n\n return merged;\n}\n\n/**\n * Build selector fragment from a variant (without className prefix)\n */\nfunction buildSelectorFromVariant(\n variant: SelectorVariant,\n selectorSuffix: string,\n): string {\n let selector = '';\n\n // Add flat modifier + pseudo selectors (sorted for canonical output)\n selector += branchToCSS([\n ...variant.modifierConditions,\n ...variant.pseudoConditions,\n ]);\n\n // Add selector groups (:is()/:not() on element)\n for (const group of variant.selectorGroups) {\n selector += selectorGroupToCSS(group);\n }\n\n // Add parent selectors (before sub-element suffix)\n if (variant.parentGroups.length > 0) {\n selector += parentGroupsToCSS(variant.parentGroups);\n }\n\n selector += selectorSuffix;\n\n // Add own groups (:is()/:not() on sub-element)\n const ownOptimized = optimizeGroups(variant.ownGroups);\n for (const group of ownOptimized) {\n selector += selectorGroupToCSS(group);\n }\n\n return selector;\n}\n\n/**\n * Materialize a computed rule to final CSS format\n *\n * Returns an array because OR conditions may generate multiple CSS rules\n * (when different branches have different at-rules)\n */\nfunction materializeComputedRule(rule: ComputedRule): CSSRule[] {\n const components = conditionToCSS(rule.condition);\n\n if (components.isImpossible || components.variants.length === 0) {\n return [];\n }\n\n const declarations = Object.entries(rule.declarations)\n .map(([prop, value]) => `${prop}: ${value};`)\n .join(' ');\n\n // Helper to get root prefix key for grouping\n const getRootPrefixKey = (variant: SelectorVariant): string => {\n return rootGroupsToCSS(variant.rootGroups) || '';\n };\n\n // Group variants by their at-rules + startingStyle (variants with same context can be combined with commas)\n const byAtRules = new Map<\n string,\n {\n variants: SelectorVariant[];\n atRules: string[];\n rootPrefix?: string;\n startingStyle?: boolean;\n }\n >();\n\n for (const variant of components.variants) {\n const atRules = buildAtRulesFromVariant(variant);\n const startingStyle = variant.startingStyle;\n const key =\n atRules.sort().join('|||') +\n '###' +\n getRootPrefixKey(variant) +\n '###' +\n (startingStyle ? '1' : '0');\n\n const group = byAtRules.get(key);\n if (group) {\n group.variants.push(variant);\n } else {\n byAtRules.set(key, {\n variants: [variant],\n atRules,\n rootPrefix: rootGroupsToCSS(variant.rootGroups),\n startingStyle: startingStyle || undefined,\n });\n }\n }\n\n // Generate one CSSRule per at-rules group\n const rules: CSSRule[] = [];\n for (const [, group] of byAtRules) {\n // Merge variants that differ only in flat modifier/pseudo conditions\n // into :is() groups before building selector strings\n const mergedVariants = mergeVariantsIntoSelectorGroups(group.variants);\n\n // Build selector fragments for each variant (will be joined with className later)\n const selectorFragments = mergedVariants.map((v) =>\n buildSelectorFromVariant(v, rule.selectorSuffix),\n );\n\n // Store as array if multiple, string if single\n const selector =\n selectorFragments.length === 1 ? selectorFragments[0] : selectorFragments;\n\n const cssRule: CSSRule = {\n selector,\n declarations,\n };\n\n if (group.atRules.length > 0) {\n cssRule.atRules = group.atRules;\n }\n\n if (group.rootPrefix) {\n cssRule.rootPrefix = group.rootPrefix;\n }\n\n if (group.startingStyle) {\n cssRule.startingStyle = true;\n }\n\n rules.push(cssRule);\n }\n\n return rules;\n}\n\n// ============================================================================\n// StyleResult merging (group by selector + at-rules)\n// ============================================================================\n\n/**\n * Merge StyleResult entries that share the same selector and at-rules,\n * concatenating their declarations into a single rule.\n *\n * This reduces CSS output size when many style keys (e.g. design tokens)\n * resolve to the same selector/state combination.\n */\nfunction mergeStyleResults(results: StyleResult[]): StyleResult[] {\n if (results.length <= 1) return results;\n\n const groups = new Map<string, StyleResult>();\n\n for (const result of results) {\n const atKey = result.atRules?.join('|') ?? '';\n const key = `${atKey}||${result.selector}||${result.startingStyle ? '1' : '0'}`;\n\n const existing = groups.get(key);\n if (existing) {\n existing.declarations = existing.declarations\n ? `${existing.declarations} ${result.declarations}`\n : result.declarations;\n } else {\n groups.set(key, { ...result });\n }\n }\n\n return Array.from(groups.values());\n}\n\n// ============================================================================\n// Public API: renderStyles (compatible with old API)\n// ============================================================================\n\n/**\n * Options for renderStyles when using direct selector mode.\n */\nexport interface RenderStylesOptions {\n /**\n * Whether to double the class selector for increased specificity.\n * When true, `.myClass` becomes `.myClass.myClass` for higher specificity.\n *\n * @default false - User-provided selectors are not doubled.\n *\n * Note: This only applies when a classNameOrSelector is provided.\n * When renderStyles returns RenderResult with needsClassName=true,\n * the injector handles doubling automatically.\n */\n doubleSelector?: boolean;\n}\n\n/**\n * Render styles to CSS rules.\n *\n * When called without classNameOrSelector, returns RenderResult with needsClassName=true.\n * When called with a selector/className string, returns StyleResult[] for direct injection.\n */\nexport function renderStyles(\n styles?: Styles,\n classNameOrSelector?: undefined,\n options?: undefined,\n pipelineCacheKey?: string,\n): RenderResult;\nexport function renderStyles(\n styles: Styles | undefined,\n classNameOrSelector: string,\n options?: RenderStylesOptions,\n): StyleResult[];\nexport function renderStyles(\n styles?: Styles,\n classNameOrSelector?: string,\n options?: RenderStylesOptions,\n pipelineCacheKey?: string,\n): RenderResult | StyleResult[] {\n // Check if we have a direct selector/className\n const directSelector = !!classNameOrSelector;\n\n // Check cache first when a pre-computed key is available.\n // This allows callers to skip building the styles object on cache hit.\n let rules: CSSRule[] | undefined;\n if (pipelineCacheKey) {\n rules = pipelineCache.get(pipelineCacheKey);\n }\n\n if (!rules && !styles) {\n return directSelector ? [] : { rules: [] };\n }\n\n // Use pre-computed cache key when available (from chunk path),\n // falling back to stringifyStyles for direct renderStyles() calls\n const cacheKey = pipelineCacheKey || stringifyStyles(styles!);\n if (!rules) {\n rules = pipelineCache.get(cacheKey);\n }\n\n if (!rules) {\n // styles is guaranteed non-null here: early return above handles (!rules && !styles)\n const parserContext = createStateParserContext(styles!);\n rules = runPipeline(styles!, parserContext);\n pipelineCache.set(cacheKey, rules);\n }\n\n // Direct selector/className mode: return StyleResult[] directly\n if (directSelector) {\n const shouldDouble = options?.doubleSelector ?? false;\n\n const results = rules.map((rule): StyleResult => {\n // Handle selector as array (OR conditions) or string\n const selectorParts = Array.isArray(rule.selector)\n ? rule.selector\n : rule.selector\n ? [rule.selector]\n : [''];\n\n const finalSelector = selectorParts\n .map((part) => {\n let sel = part\n ? `${classNameOrSelector}${part}`\n : classNameOrSelector;\n\n // Double class selector for increased specificity if requested\n // This is used when the caller explicitly wants higher specificity\n if (shouldDouble && sel.startsWith('.')) {\n const classMatch = sel.match(/^\\.[a-zA-Z_-][a-zA-Z0-9_-]*/);\n if (classMatch) {\n const baseClass = classMatch[0];\n sel = baseClass + sel;\n }\n }\n\n // Handle root prefix for this selector\n if (rule.rootPrefix) {\n sel = `${rule.rootPrefix} ${sel}`;\n }\n\n return sel;\n })\n .join(', ');\n\n const result: StyleResult = {\n selector: finalSelector,\n declarations: rule.declarations,\n };\n\n if (rule.atRules && rule.atRules.length > 0) {\n result.atRules = rule.atRules;\n }\n\n if (rule.startingStyle) {\n result.startingStyle = true;\n }\n\n return result;\n });\n\n return mergeStyleResults(results);\n }\n\n // No className mode: return RenderResult with needsClassName flag\n // Normalize selector to string (join array with placeholder that injector will handle)\n return {\n rules: rules.map(\n (r): StyleResult => ({\n selector: Array.isArray(r.selector)\n ? r.selector.join('|||')\n : r.selector,\n declarations: r.declarations,\n atRules: r.atRules,\n needsClassName: true,\n rootPrefix: r.rootPrefix,\n startingStyle: r.startingStyle,\n }),\n ),\n };\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\nexport type { ConditionNode } from './conditions';\nexport { and, or, not, trueCondition, falseCondition } from './conditions';\nexport { parseStateKey } from './parseStateKey';\nexport { simplifyCondition } from './simplify';\nexport { buildExclusiveConditions } from './exclusive';\nexport { conditionToCSS } from './materialize';\nexport type { CSSRule } from './materialize';\nexport { setWarningHandler, emitWarning } from './warnings';\nexport type {\n TastyWarning,\n TastyWarningCode,\n TastyWarningHandler,\n} from './warnings';\n"],"mappings":"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"}