@tenphi/tasty 0.1.0 → 0.1.2

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.
Files changed (141) hide show
  1. package/dist/_virtual/_rolldown/runtime.js +8 -0
  2. package/dist/zero/{babel.d.mts → babel.d.ts} +6 -6
  3. package/dist/zero/{babel.mjs → babel.js} +6 -6
  4. package/dist/zero/babel.js.map +1 -0
  5. package/dist/{css-writer.d.mts → zero/css-writer.d.ts} +1 -1
  6. package/dist/{css-writer.mjs → zero/css-writer.js} +1 -1
  7. package/dist/zero/css-writer.js.map +1 -0
  8. package/dist/{extractor.d.mts → zero/extractor.d.ts} +3 -2
  9. package/dist/{extractor.mjs → zero/extractor.js} +6 -6
  10. package/dist/zero/extractor.js.map +1 -0
  11. package/dist/zero/index.d.ts +3 -0
  12. package/dist/zero/{index.mjs → index.js} +2 -2
  13. package/dist/zero/{next.d.mts → next.d.ts} +1 -1
  14. package/dist/zero/{next.mjs → next.js} +2 -2
  15. package/dist/zero/next.js.map +1 -0
  16. package/package.json +16 -23
  17. package/dist/_virtual/_rolldown/runtime.mjs +0 -7
  18. package/dist/chunks/cacheKey.mjs +0 -70
  19. package/dist/chunks/cacheKey.mjs.map +0 -1
  20. package/dist/chunks/definitions.mjs +0 -260
  21. package/dist/chunks/definitions.mjs.map +0 -1
  22. package/dist/chunks/renderChunk.mjs +0 -61
  23. package/dist/chunks/renderChunk.mjs.map +0 -1
  24. package/dist/config.mjs +0 -231
  25. package/dist/config.mjs.map +0 -1
  26. package/dist/css-writer.mjs.map +0 -1
  27. package/dist/extractor.mjs.map +0 -1
  28. package/dist/injector/injector.mjs +0 -404
  29. package/dist/injector/injector.mjs.map +0 -1
  30. package/dist/injector/sheet-manager.mjs +0 -714
  31. package/dist/injector/sheet-manager.mjs.map +0 -1
  32. package/dist/injector/types.d.mts +0 -18
  33. package/dist/keyframes/index.mjs +0 -156
  34. package/dist/keyframes/index.mjs.map +0 -1
  35. package/dist/parser/classify.mjs +0 -319
  36. package/dist/parser/classify.mjs.map +0 -1
  37. package/dist/parser/const.mjs +0 -33
  38. package/dist/parser/const.mjs.map +0 -1
  39. package/dist/parser/lru.mjs +0 -109
  40. package/dist/parser/lru.mjs.map +0 -1
  41. package/dist/parser/parser.mjs +0 -116
  42. package/dist/parser/parser.mjs.map +0 -1
  43. package/dist/parser/tokenizer.mjs +0 -69
  44. package/dist/parser/tokenizer.mjs.map +0 -1
  45. package/dist/parser/types.d.mts +0 -37
  46. package/dist/parser/types.mjs +0 -46
  47. package/dist/parser/types.mjs.map +0 -1
  48. package/dist/pipeline/conditions.mjs +0 -377
  49. package/dist/pipeline/conditions.mjs.map +0 -1
  50. package/dist/pipeline/exclusive.mjs +0 -231
  51. package/dist/pipeline/exclusive.mjs.map +0 -1
  52. package/dist/pipeline/index.mjs +0 -635
  53. package/dist/pipeline/index.mjs.map +0 -1
  54. package/dist/pipeline/materialize.mjs +0 -821
  55. package/dist/pipeline/materialize.mjs.map +0 -1
  56. package/dist/pipeline/parseStateKey.mjs +0 -418
  57. package/dist/pipeline/parseStateKey.mjs.map +0 -1
  58. package/dist/pipeline/simplify.mjs +0 -557
  59. package/dist/pipeline/simplify.mjs.map +0 -1
  60. package/dist/plugins/okhsl-plugin.mjs +0 -345
  61. package/dist/plugins/okhsl-plugin.mjs.map +0 -1
  62. package/dist/plugins/types.d.mts +0 -49
  63. package/dist/properties/index.mjs +0 -141
  64. package/dist/properties/index.mjs.map +0 -1
  65. package/dist/states/index.mjs +0 -161
  66. package/dist/states/index.mjs.map +0 -1
  67. package/dist/styles/align.mjs +0 -14
  68. package/dist/styles/align.mjs.map +0 -1
  69. package/dist/styles/border.mjs +0 -114
  70. package/dist/styles/border.mjs.map +0 -1
  71. package/dist/styles/color.mjs +0 -23
  72. package/dist/styles/color.mjs.map +0 -1
  73. package/dist/styles/createStyle.mjs +0 -77
  74. package/dist/styles/createStyle.mjs.map +0 -1
  75. package/dist/styles/dimension.mjs +0 -97
  76. package/dist/styles/dimension.mjs.map +0 -1
  77. package/dist/styles/display.mjs +0 -67
  78. package/dist/styles/display.mjs.map +0 -1
  79. package/dist/styles/fade.mjs +0 -58
  80. package/dist/styles/fade.mjs.map +0 -1
  81. package/dist/styles/fill.mjs +0 -51
  82. package/dist/styles/fill.mjs.map +0 -1
  83. package/dist/styles/flow.mjs +0 -12
  84. package/dist/styles/flow.mjs.map +0 -1
  85. package/dist/styles/gap.mjs +0 -37
  86. package/dist/styles/gap.mjs.map +0 -1
  87. package/dist/styles/height.mjs +0 -20
  88. package/dist/styles/height.mjs.map +0 -1
  89. package/dist/styles/index.mjs +0 -9
  90. package/dist/styles/index.mjs.map +0 -1
  91. package/dist/styles/inset.mjs +0 -142
  92. package/dist/styles/inset.mjs.map +0 -1
  93. package/dist/styles/justify.mjs +0 -14
  94. package/dist/styles/justify.mjs.map +0 -1
  95. package/dist/styles/margin.mjs +0 -96
  96. package/dist/styles/margin.mjs.map +0 -1
  97. package/dist/styles/outline.mjs +0 -65
  98. package/dist/styles/outline.mjs.map +0 -1
  99. package/dist/styles/padding.mjs +0 -96
  100. package/dist/styles/padding.mjs.map +0 -1
  101. package/dist/styles/predefined.mjs +0 -232
  102. package/dist/styles/predefined.mjs.map +0 -1
  103. package/dist/styles/preset.mjs +0 -126
  104. package/dist/styles/preset.mjs.map +0 -1
  105. package/dist/styles/radius.mjs +0 -51
  106. package/dist/styles/radius.mjs.map +0 -1
  107. package/dist/styles/scrollbar.mjs +0 -105
  108. package/dist/styles/scrollbar.mjs.map +0 -1
  109. package/dist/styles/shadow.mjs +0 -24
  110. package/dist/styles/shadow.mjs.map +0 -1
  111. package/dist/styles/styledScrollbar.mjs +0 -38
  112. package/dist/styles/styledScrollbar.mjs.map +0 -1
  113. package/dist/styles/transition.mjs +0 -138
  114. package/dist/styles/transition.mjs.map +0 -1
  115. package/dist/styles/types.d.mts +0 -492
  116. package/dist/styles/width.mjs +0 -20
  117. package/dist/styles/width.mjs.map +0 -1
  118. package/dist/utils/cache-wrapper.mjs +0 -26
  119. package/dist/utils/cache-wrapper.mjs.map +0 -1
  120. package/dist/utils/case-converter.mjs +0 -8
  121. package/dist/utils/case-converter.mjs.map +0 -1
  122. package/dist/utils/hsl-to-rgb.mjs +0 -38
  123. package/dist/utils/hsl-to-rgb.mjs.map +0 -1
  124. package/dist/utils/is-dev-env.mjs +0 -19
  125. package/dist/utils/is-dev-env.mjs.map +0 -1
  126. package/dist/utils/merge-styles.mjs +0 -146
  127. package/dist/utils/merge-styles.mjs.map +0 -1
  128. package/dist/utils/okhsl-to-rgb.mjs +0 -296
  129. package/dist/utils/okhsl-to-rgb.mjs.map +0 -1
  130. package/dist/utils/process-tokens.mjs +0 -28
  131. package/dist/utils/process-tokens.mjs.map +0 -1
  132. package/dist/utils/resolve-recipes.mjs +0 -143
  133. package/dist/utils/resolve-recipes.mjs.map +0 -1
  134. package/dist/utils/string.mjs +0 -8
  135. package/dist/utils/string.mjs.map +0 -1
  136. package/dist/utils/styles.d.mts +0 -18
  137. package/dist/utils/styles.mjs +0 -346
  138. package/dist/utils/styles.mjs.map +0 -1
  139. package/dist/zero/babel.mjs.map +0 -1
  140. package/dist/zero/index.d.mts +0 -3
  141. package/dist/zero/next.mjs.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"sheet-manager.mjs","names":[],"sources":["../../src/injector/sheet-manager.ts"],"sourcesContent":["import { createStyle, STYLE_HANDLER_MAP } from '../styles';\n\nimport type {\n CacheMetrics,\n KeyframesInfo,\n KeyframesSteps,\n RawCSSInfo,\n RawCSSResult,\n RootRegistry,\n RuleInfo,\n SheetInfo,\n StyleInjectorConfig,\n StyleRule,\n} from './types';\n\nimport type {\n CSSMap,\n StyleHandler,\n StyleValueStateMap,\n} from '../utils/styles';\n\nexport class SheetManager {\n private rootRegistries = new WeakMap<Document | ShadowRoot, RootRegistry>();\n private config: StyleInjectorConfig;\n /** Dedicated style elements for raw CSS per root */\n private rawStyleElements = new WeakMap<\n Document | ShadowRoot,\n HTMLStyleElement\n >();\n /** Tracking for raw CSS blocks per root */\n private rawCSSBlocks = new WeakMap<\n Document | ShadowRoot,\n Map<string, RawCSSInfo>\n >();\n /** Counter for generating unique raw CSS IDs */\n private rawCSSCounter = 0;\n\n constructor(config: StyleInjectorConfig) {\n this.config = config;\n }\n\n /**\n * Get or create registry for a root (Document or ShadowRoot)\n */\n getRegistry(root: Document | ShadowRoot): RootRegistry {\n let registry = this.rootRegistries.get(root);\n\n if (!registry) {\n const metrics: CacheMetrics | undefined = this.config.devMode\n ? {\n hits: 0,\n misses: 0,\n bulkCleanups: 0,\n totalInsertions: 0,\n totalUnused: 0,\n stylesCleanedUp: 0,\n cleanupHistory: [],\n startTime: Date.now(),\n }\n : undefined;\n\n registry = {\n sheets: [],\n refCounts: new Map(),\n rules: new Map(),\n cacheKeyToClassName: new Map(),\n ruleTextSet: new Set<string>(),\n bulkCleanupTimeout: null,\n cleanupCheckTimeout: null,\n metrics,\n classCounter: 0,\n keyframesCache: new Map(),\n keyframesNameToContent: new Map(),\n keyframesCounter: 0,\n injectedProperties: new Map<string, string>(),\n globalRules: new Map(),\n } as unknown as RootRegistry;\n\n this.rootRegistries.set(root, registry);\n }\n\n return registry;\n }\n\n /**\n * Create a new stylesheet for the registry\n */\n createSheet(registry: RootRegistry, root: Document | ShadowRoot): SheetInfo {\n const sheet = this.createStyleElement(root);\n\n const sheetInfo: SheetInfo = {\n sheet,\n ruleCount: 0,\n holes: [],\n };\n\n registry.sheets.push(sheetInfo);\n return sheetInfo;\n }\n\n /**\n * Create a style element and append to document\n */\n private createStyleElement(root: Document | ShadowRoot): HTMLStyleElement {\n const style =\n (root as Document).createElement?.('style') ||\n document.createElement('style');\n\n if (this.config.nonce) {\n style.nonce = this.config.nonce;\n }\n\n style.setAttribute('data-tasty', '');\n\n // Append to head or shadow root\n if ('head' in root && root.head) {\n root.head.appendChild(style);\n } else if ('appendChild' in root) {\n root.appendChild(style);\n } else {\n document.head.appendChild(style);\n }\n\n // Verify it was actually added - log only if there's a problem and we're not using forceTextInjection\n if (!style.isConnected && !this.config.forceTextInjection) {\n console.error('SheetManager: Style element failed to connect to DOM!', {\n parentNode: style.parentNode?.nodeName,\n isConnected: style.isConnected,\n });\n }\n\n return style;\n }\n\n /**\n * Insert CSS rules as a single block\n */\n insertRule(\n registry: RootRegistry,\n flattenedRules: StyleRule[],\n className: string,\n root: Document | ShadowRoot,\n ): RuleInfo | null {\n // Find or create a sheet with available space\n let targetSheet = this.findAvailableSheet(registry, root);\n\n if (!targetSheet) {\n targetSheet = this.createSheet(registry, root);\n }\n\n const sheetIndex = registry.sheets.indexOf(targetSheet);\n\n try {\n // Group rules by selector and at-rules to combine declarations\n const groupedRules: StyleRule[] = [];\n const groupMap = new Map<\n string,\n {\n idx: number;\n selector: string;\n atRules?: string[];\n declarations: string;\n }\n >();\n\n const atKey = (at?: string[]) => (at && at.length ? at.join('|') : '');\n\n flattenedRules.forEach((r) => {\n const key = `${atKey(r.atRules)}||${r.selector}`;\n const existing = groupMap.get(key);\n if (existing) {\n // Append declarations, preserving order\n existing.declarations = existing.declarations\n ? `${existing.declarations} ${r.declarations}`\n : r.declarations;\n } else {\n groupMap.set(key, {\n idx: groupedRules.length,\n selector: r.selector,\n atRules: r.atRules,\n declarations: r.declarations,\n });\n groupedRules.push({ ...r });\n }\n });\n\n // Normalize groupedRules from map (with merged declarations)\n groupMap.forEach((val) => {\n groupedRules[val.idx] = {\n selector: val.selector,\n atRules: val.atRules,\n declarations: val.declarations,\n } as StyleRule;\n });\n\n // Insert grouped rules\n const insertedRuleTexts: string[] = [];\n const insertedIndices: number[] = []; // Track exact indices\n // Calculate rule index atomically right before insertion to prevent race conditions\n let currentRuleIndex = this.findAvailableRuleIndex(targetSheet);\n let firstInsertedIndex: number | null = null;\n let lastInsertedIndex: number | null = null;\n\n for (const rule of groupedRules) {\n const declarations = rule.declarations;\n const baseRule = `${rule.selector} { ${declarations} }`;\n\n // Wrap with at-rules if present\n let fullRule = baseRule;\n if (rule.atRules && rule.atRules.length > 0) {\n fullRule = rule.atRules.reduce(\n (css, atRule) => `${atRule} { ${css} }`,\n baseRule,\n );\n }\n\n // Insert individual rule into style element\n const styleElement = targetSheet.sheet;\n const styleSheet = styleElement.sheet;\n\n if (styleSheet && !this.config.forceTextInjection) {\n // Calculate index atomically for each rule to prevent concurrent insertion races\n const maxIndex = styleSheet.cssRules.length;\n const atomicRuleIndex = this.findAvailableRuleIndex(targetSheet);\n const safeIndex = Math.min(Math.max(0, atomicRuleIndex), maxIndex);\n\n // Helper: split comma-separated selectors safely (ignores commas inside [] () \" ')\n const splitSelectorsSafely = (selectorList: string): string[] => {\n const parts: string[] = [];\n let buf = '';\n let depthSq = 0; // [] depth\n let depthPar = 0; // () depth\n let inStr: '\"' | \"'\" | '' = '';\n for (let i = 0; i < selectorList.length; i++) {\n const ch = selectorList[i];\n if (inStr) {\n if (ch === inStr && selectorList[i - 1] !== '\\\\') {\n inStr = '';\n }\n buf += ch;\n continue;\n }\n if (ch === '\"' || ch === \"'\") {\n inStr = ch as '\"' | \"'\";\n buf += ch;\n continue;\n }\n if (ch === '[') depthSq++;\n else if (ch === ']') depthSq = Math.max(0, depthSq - 1);\n else if (ch === '(') depthPar++;\n else if (ch === ')') depthPar = Math.max(0, depthPar - 1);\n\n if (ch === ',' && depthSq === 0 && depthPar === 0) {\n const part = buf.trim();\n if (part) parts.push(part);\n buf = '';\n } else {\n buf += ch;\n }\n }\n const tail = buf.trim();\n if (tail) parts.push(tail);\n return parts;\n };\n\n try {\n styleSheet.insertRule(fullRule, safeIndex);\n // Update sheet ruleCount immediately to prevent concurrent race conditions\n targetSheet.ruleCount++;\n insertedIndices.push(safeIndex); // Track this index\n if (firstInsertedIndex == null) firstInsertedIndex = safeIndex;\n lastInsertedIndex = safeIndex;\n currentRuleIndex = safeIndex + 1;\n } catch (e) {\n // If the browser rejects the combined selector (e.g., vendor pseudo-elements),\n // try to split and insert each selector independently. Skip unsupported ones.\n const selectors = splitSelectorsSafely(rule.selector);\n if (selectors.length > 1) {\n let anyInserted = false;\n for (const sel of selectors) {\n const singleBase = `${sel} { ${declarations} }`;\n let singleRule = singleBase;\n if (rule.atRules && rule.atRules.length > 0) {\n singleRule = rule.atRules.reduce(\n (css, atRule) => `${atRule} { ${css} }`,\n singleBase,\n );\n }\n\n try {\n // Calculate index atomically for each individual selector insertion\n const maxIdx = styleSheet.cssRules.length;\n const atomicIdx = this.findAvailableRuleIndex(targetSheet);\n const idx = Math.min(Math.max(0, atomicIdx), maxIdx);\n styleSheet.insertRule(singleRule, idx);\n // Update sheet ruleCount immediately\n targetSheet.ruleCount++;\n insertedIndices.push(idx); // Track this index\n if (firstInsertedIndex == null) firstInsertedIndex = idx;\n lastInsertedIndex = idx;\n currentRuleIndex = idx + 1;\n anyInserted = true;\n } catch (singleErr) {\n // Skip unsupported selector in this engine (e.g., ::-moz-selection in Blink)\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n '[tasty] Browser rejected CSS rule:',\n singleRule,\n singleErr,\n );\n }\n }\n }\n // If none inserted, continue without throwing to avoid aborting the whole batch\n if (!anyInserted) {\n // noop: all selectors invalid here; safe to skip\n }\n } else {\n // Single selector failed — skip it silently (likely unsupported in this engine)\n if (process.env.NODE_ENV !== 'production') {\n console.warn('[tasty] Browser rejected CSS rule:', fullRule, e);\n }\n }\n }\n } else {\n // Use textContent (either as fallback or when forceTextInjection is enabled)\n // Calculate index atomically for textContent insertion too\n const atomicRuleIndex = this.findAvailableRuleIndex(targetSheet);\n styleElement.textContent =\n (styleElement.textContent || '') + '\\n' + fullRule;\n // Update sheet ruleCount immediately\n targetSheet.ruleCount++;\n insertedIndices.push(atomicRuleIndex); // Track this index\n if (firstInsertedIndex == null) firstInsertedIndex = atomicRuleIndex;\n lastInsertedIndex = atomicRuleIndex;\n currentRuleIndex = atomicRuleIndex + 1;\n }\n\n // CRITICAL DEBUG: Verify the style element is in DOM only if there are issues and we're not using forceTextInjection\n if (!styleElement.parentNode && !this.config.forceTextInjection) {\n console.error(\n 'SheetManager: Style element is NOT in DOM! This is the problem!',\n {\n className,\n ruleIndex: currentRuleIndex,\n },\n );\n }\n\n // Dev-only: store cssText for debugging tools\n if (this.config.devMode) {\n insertedRuleTexts.push(fullRule);\n try {\n registry.ruleTextSet.add(fullRule);\n } catch {\n // noop: defensive in case ruleTextSet is unavailable\n }\n }\n // currentRuleIndex already adjusted above\n }\n\n // Sheet ruleCount is now updated immediately after each insertion\n // No need for deferred update logic\n\n if (insertedIndices.length === 0) {\n return null;\n }\n\n return {\n className,\n ruleIndex: firstInsertedIndex ?? 0,\n sheetIndex,\n cssText: this.config.devMode ? insertedRuleTexts : undefined,\n endRuleIndex: lastInsertedIndex ?? firstInsertedIndex ?? 0,\n indices: insertedIndices,\n };\n } catch (error) {\n console.warn('Failed to insert CSS rules:', error, {\n flattenedRules,\n className,\n });\n return null;\n }\n }\n\n /**\n * Insert global CSS rules\n */\n insertGlobalRule(\n registry: RootRegistry,\n flattenedRules: StyleRule[],\n globalKey: string,\n root: Document | ShadowRoot,\n ): RuleInfo | null {\n // Insert the rule using the same mechanism as regular rules\n const ruleInfo = this.insertRule(registry, flattenedRules, globalKey, root);\n\n // Track global rules for index adjustment\n if (ruleInfo) {\n registry.globalRules.set(globalKey, ruleInfo);\n }\n\n return ruleInfo;\n }\n\n /**\n * Delete a global CSS rule by key\n */\n public deleteGlobalRule(registry: RootRegistry, globalKey: string): void {\n const ruleInfo = registry.globalRules.get(globalKey);\n if (!ruleInfo) {\n return;\n }\n\n // Delete the rule using the standard deletion mechanism\n this.deleteRule(registry, ruleInfo);\n\n // Remove from global rules tracking\n registry.globalRules.delete(globalKey);\n }\n\n /**\n * Adjust rule indices after deletion to account for shifting\n */\n private adjustIndicesAfterDeletion(\n registry: RootRegistry,\n sheetIndex: number,\n startIdx: number,\n endIdx: number,\n deleteCount: number,\n deletedRuleInfo: RuleInfo,\n deletedIndices?: number[],\n ): void {\n try {\n const sortedDeleted =\n deletedIndices && deletedIndices.length > 0\n ? [...deletedIndices].sort((a, b) => a - b)\n : null;\n const countDeletedBefore = (sorted: number[], idx: number): number => {\n let shift = 0;\n for (const delIdx of sorted) {\n if (delIdx < idx) shift++;\n else break;\n }\n return shift;\n };\n // Helper function to adjust a single RuleInfo\n const adjustRuleInfo = (info: RuleInfo): void => {\n if (info === deletedRuleInfo) return; // Skip the deleted rule\n if (info.sheetIndex !== sheetIndex) return; // Different sheet\n\n if (!info.indices || info.indices.length === 0) {\n return;\n }\n\n if (sortedDeleted) {\n // Adjust each index based on how many deleted indices are before it\n info.indices = info.indices.map((idx) => {\n return idx - countDeletedBefore(sortedDeleted, idx);\n });\n } else {\n // Contiguous deletion: shift indices after the deleted range\n info.indices = info.indices.map((idx) =>\n idx > endIdx ? Math.max(0, idx - deleteCount) : idx,\n );\n }\n\n // Update ruleIndex and endRuleIndex to match adjusted indices\n if (info.indices.length > 0) {\n info.ruleIndex = Math.min(...info.indices);\n info.endRuleIndex = Math.max(...info.indices);\n }\n };\n\n // Adjust active rules\n for (const info of registry.rules.values()) {\n adjustRuleInfo(info);\n }\n\n // Adjust global rules\n for (const info of registry.globalRules.values()) {\n adjustRuleInfo(info);\n }\n\n // No need to separately adjust unused rules since they're part of the rules Map\n\n // Adjust keyframes indices stored in cache\n for (const entry of registry.keyframesCache.values()) {\n const ki = entry.info as KeyframesInfo;\n if (ki.sheetIndex !== sheetIndex) continue;\n if (sortedDeleted) {\n const shift = countDeletedBefore(sortedDeleted, ki.ruleIndex);\n if (shift > 0) {\n ki.ruleIndex = Math.max(0, ki.ruleIndex - shift);\n }\n } else if (ki.ruleIndex > endIdx) {\n ki.ruleIndex = Math.max(0, ki.ruleIndex - deleteCount);\n }\n }\n } catch {\n // Defensive: do not let index adjustments crash cleanup\n }\n }\n\n /**\n * Delete a CSS rule from the sheet\n */\n deleteRule(registry: RootRegistry, ruleInfo: RuleInfo): void {\n const sheet = registry.sheets[ruleInfo.sheetIndex];\n\n if (!sheet) {\n return;\n }\n\n try {\n const texts: string[] =\n this.config.devMode && Array.isArray(ruleInfo.cssText)\n ? ruleInfo.cssText.slice()\n : [];\n\n const styleElement = sheet.sheet;\n const styleSheet = styleElement.sheet;\n\n if (styleSheet) {\n const rules = styleSheet.cssRules;\n\n // Use exact indices if available, otherwise fall back to range\n if (ruleInfo.indices && ruleInfo.indices.length > 0) {\n // NEW: Delete using exact tracked indices\n const sortedIndices = [...ruleInfo.indices].sort((a, b) => b - a); // Sort descending\n const deletedIndices: number[] = [];\n\n for (const idx of sortedIndices) {\n if (idx >= 0 && idx < styleSheet.cssRules.length) {\n try {\n styleSheet.deleteRule(idx);\n deletedIndices.push(idx);\n } catch (e) {\n console.warn(`Failed to delete rule at index ${idx}:`, e);\n }\n }\n }\n\n sheet.ruleCount = Math.max(\n 0,\n sheet.ruleCount - deletedIndices.length,\n );\n\n // Adjust indices for all other rules\n if (deletedIndices.length > 0) {\n this.adjustIndicesAfterDeletion(\n registry,\n ruleInfo.sheetIndex,\n Math.min(...deletedIndices),\n Math.max(...deletedIndices),\n deletedIndices.length,\n ruleInfo,\n deletedIndices,\n );\n }\n } else {\n // FALLBACK: Use old range-based deletion for backwards compatibility\n const startIdx = Math.max(0, ruleInfo.ruleIndex);\n const endIdx = Math.min(\n rules.length - 1,\n Number.isFinite(ruleInfo.endRuleIndex as number)\n ? (ruleInfo.endRuleIndex as number)\n : startIdx,\n );\n\n if (Number.isFinite(startIdx) && endIdx >= startIdx) {\n const deleteCount = endIdx - startIdx + 1;\n for (let idx = endIdx; idx >= startIdx; idx--) {\n if (idx < 0 || idx >= styleSheet.cssRules.length) continue;\n styleSheet.deleteRule(idx);\n }\n sheet.ruleCount = Math.max(0, sheet.ruleCount - deleteCount);\n\n // After deletion, all subsequent rule indices shift left by deleteCount.\n // We must adjust stored indices for all other RuleInfo within the same sheet.\n this.adjustIndicesAfterDeletion(\n registry,\n ruleInfo.sheetIndex,\n startIdx,\n endIdx,\n deleteCount,\n ruleInfo,\n );\n }\n }\n }\n\n // Dev-only: remove cssText entries from validation set\n if (this.config.devMode && texts.length) {\n try {\n for (const text of texts) {\n registry.ruleTextSet.delete(text);\n }\n } catch {\n // noop\n }\n }\n } catch (error) {\n console.warn('Failed to delete CSS rule:', error);\n }\n }\n\n /**\n * Find a sheet with available space or return null\n */\n private findAvailableSheet(\n registry: RootRegistry,\n _root: Document | ShadowRoot,\n ): SheetInfo | null {\n const maxRules = this.config.maxRulesPerSheet;\n\n if (!maxRules) {\n // No limit, use the last sheet if it exists\n const lastSheet = registry.sheets[registry.sheets.length - 1];\n return lastSheet || null;\n }\n\n // Find sheet with space\n for (const sheet of registry.sheets) {\n if (sheet.ruleCount < maxRules) {\n return sheet;\n }\n }\n\n return null; // No available sheet found\n }\n\n /**\n * Find an available rule index in the sheet\n */\n findAvailableRuleIndex(sheet: SheetInfo): number {\n // Always append to the end - CSS doesn't have holes\n return sheet.ruleCount;\n }\n\n /**\n * Schedule bulk cleanup of all unused styles (non-stacking)\n */\n private scheduleBulkCleanup(registry: RootRegistry): void {\n // Clear any existing timeout to prevent stacking\n if (registry.bulkCleanupTimeout) {\n if (\n this.config.idleCleanup &&\n typeof cancelIdleCallback !== 'undefined'\n ) {\n cancelIdleCallback(registry.bulkCleanupTimeout as unknown as number);\n } else {\n clearTimeout(registry.bulkCleanupTimeout);\n }\n registry.bulkCleanupTimeout = null;\n }\n\n const performCleanup = () => {\n this.performBulkCleanup(registry);\n registry.bulkCleanupTimeout = null;\n };\n\n if (this.config.idleCleanup && typeof requestIdleCallback !== 'undefined') {\n registry.bulkCleanupTimeout = requestIdleCallback(performCleanup);\n } else {\n const delay = this.config.bulkCleanupDelay || 5000;\n registry.bulkCleanupTimeout = setTimeout(performCleanup, delay);\n }\n }\n\n /**\n * Force cleanup of unused styles\n */\n public forceCleanup(registry: RootRegistry): void {\n this.performBulkCleanup(registry, true);\n }\n\n /**\n * Perform bulk cleanup of all unused styles\n */\n private performBulkCleanup(registry: RootRegistry, cleanupAll = false): void {\n const cleanupStartTime = Date.now();\n\n // Calculate unused rules dynamically: rules that have refCount = 0\n const unusedClassNames = Array.from(registry.refCounts.entries())\n .filter(([, refCount]) => refCount === 0)\n .map(([className]) => className);\n\n if (unusedClassNames.length === 0) return;\n\n // Build candidates list - no age filtering needed\n const candidates = unusedClassNames.map((className) => {\n const ruleInfo = registry.rules.get(className)!; // We know it exists\n return {\n className,\n ruleInfo,\n };\n });\n\n if (candidates.length === 0) return;\n\n // Limit deletion scope per run (batch ratio) unless cleanupAll is true\n let selected = candidates;\n if (!cleanupAll) {\n const ratio = this.config.bulkCleanupBatchRatio ?? 0.5;\n const limit = Math.max(\n 1,\n Math.floor(candidates.length * Math.min(1, Math.max(0, ratio))),\n );\n selected = candidates.slice(0, limit);\n }\n\n let cleanedUpCount = 0;\n let totalCssSize = 0;\n let totalRulesDeleted = 0;\n\n // Group by sheet for efficient deletion\n const rulesBySheet = new Map<\n number,\n { className: string; ruleInfo: RuleInfo }[]\n >();\n\n // Calculate CSS size before deletion and group rules\n for (const { className, ruleInfo } of selected) {\n const sheetIndex = ruleInfo.sheetIndex;\n\n // Dev-only metrics: estimate CSS size and rule count if available\n if (this.config.devMode && Array.isArray(ruleInfo.cssText)) {\n const cssSize = ruleInfo.cssText.reduce(\n (total, css) => total + css.length,\n 0,\n );\n totalCssSize += cssSize;\n totalRulesDeleted += ruleInfo.cssText.length;\n }\n\n if (!rulesBySheet.has(sheetIndex)) {\n rulesBySheet.set(sheetIndex, []);\n }\n rulesBySheet.get(sheetIndex)!.push({ className, ruleInfo });\n }\n\n // Delete rules from each sheet (in reverse order to preserve indices)\n for (const [_sheetIndex, rulesInSheet] of rulesBySheet) {\n // Sort by rule index in descending order for safe deletion\n rulesInSheet.sort((a, b) => b.ruleInfo.ruleIndex - a.ruleInfo.ruleIndex);\n\n for (const { className, ruleInfo } of rulesInSheet) {\n // SAFETY 1: Double-check refCount is still 0\n const currentRefCount = registry.refCounts.get(className) || 0;\n if (currentRefCount > 0) {\n // Class became active again; do not delete\n continue;\n }\n\n // SAFETY 2: Ensure rule wasn't replaced\n // Between scheduling and execution a class may have been replaced with a new RuleInfo\n const currentInfo = registry.rules.get(className);\n if (currentInfo !== ruleInfo) {\n // Rule was replaced; skip deletion of the old reference\n continue;\n }\n\n // SAFETY 3: Verify the sheet element is still valid and accessible\n const sheetInfo = registry.sheets[ruleInfo.sheetIndex];\n if (!sheetInfo || !sheetInfo.sheet) {\n // Sheet was removed or corrupted; skip this rule\n continue;\n }\n\n // SAFETY 4: Verify the stylesheet itself is accessible\n const styleSheet = sheetInfo.sheet.sheet;\n if (!styleSheet) {\n // Stylesheet not available; skip this rule\n continue;\n }\n\n // SAFETY 5: Verify rule index is still within valid range\n const maxRuleIndex = styleSheet.cssRules.length - 1;\n const startIdx = ruleInfo.ruleIndex;\n const endIdx = ruleInfo.endRuleIndex ?? ruleInfo.ruleIndex;\n\n if (startIdx < 0 || endIdx > maxRuleIndex || startIdx > endIdx) {\n // Rule indices are out of bounds; skip this rule\n continue;\n }\n\n // All safety checks passed - proceed with deletion\n this.deleteRule(registry, ruleInfo);\n registry.rules.delete(className);\n registry.refCounts.delete(className);\n\n // Clean up cache key mappings that point to this className\n const keysToDelete: string[] = [];\n for (const [\n key,\n mappedClassName,\n ] of registry.cacheKeyToClassName.entries()) {\n if (mappedClassName === className) {\n keysToDelete.push(key);\n }\n }\n for (const key of keysToDelete) {\n registry.cacheKeyToClassName.delete(key);\n }\n cleanedUpCount++;\n }\n }\n\n // Update metrics\n if (registry.metrics) {\n registry.metrics.bulkCleanups++;\n registry.metrics.stylesCleanedUp += cleanedUpCount;\n\n // Add detailed cleanup stats to history\n registry.metrics.cleanupHistory.push({\n timestamp: cleanupStartTime,\n classesDeleted: cleanedUpCount,\n cssSize: totalCssSize,\n rulesDeleted: totalRulesDeleted,\n });\n }\n }\n\n /**\n * Get total number of rules across all sheets\n */\n getTotalRuleCount(registry: RootRegistry): number {\n return registry.sheets.reduce(\n (total, sheet) => total + sheet.ruleCount - sheet.holes.length,\n 0,\n );\n }\n\n /**\n * Get CSS text from all sheets (for SSR)\n */\n getCssText(registry: RootRegistry): string {\n const cssChunks: string[] = [];\n\n for (const sheet of registry.sheets) {\n try {\n const styleElement = sheet.sheet;\n if (styleElement.textContent) {\n cssChunks.push(styleElement.textContent);\n } else if (styleElement.sheet) {\n const rules = Array.from(styleElement.sheet.cssRules);\n cssChunks.push(rules.map((rule) => rule.cssText).join('\\n'));\n }\n } catch (error) {\n console.warn('Failed to read CSS from sheet:', error);\n }\n }\n\n return cssChunks.join('\\n');\n }\n\n /**\n * Get cache performance metrics\n */\n getMetrics(registry: RootRegistry): CacheMetrics | null {\n if (!registry.metrics) return null;\n\n // Calculate unusedHits on demand - only count CSS rules since keyframes are disposed immediately\n const unusedRulesCount = Array.from(registry.refCounts.values()).filter(\n (count) => count === 0,\n ).length;\n\n return {\n ...registry.metrics,\n unusedHits: unusedRulesCount,\n };\n }\n\n /**\n * Reset cache performance metrics\n */\n resetMetrics(registry: RootRegistry): void {\n if (registry.metrics) {\n registry.metrics = {\n hits: 0,\n misses: 0,\n bulkCleanups: 0,\n totalInsertions: 0,\n totalUnused: 0,\n stylesCleanedUp: 0,\n cleanupHistory: [],\n startTime: Date.now(),\n };\n }\n }\n\n /**\n * Convert keyframes steps to CSS string\n */\n private stepsToCSS(steps: KeyframesSteps): string {\n const rules: string[] = [];\n\n for (const [key, value] of Object.entries(steps)) {\n // Support raw CSS strings for backwards compatibility\n if (typeof value === 'string') {\n rules.push(`${key} { ${value.trim()} }`);\n continue;\n }\n\n // Treat value as a style map and process via tasty style handlers\n const styleMap = (value || {}) as StyleValueStateMap;\n\n // Build a deterministic handler queue based on present style keys\n const styleNames = Object.keys(styleMap).sort();\n const handlerQueue: StyleHandler[] = [];\n const seenHandlers = new Set<StyleHandler>();\n\n styleNames.forEach((styleName) => {\n let handlers = STYLE_HANDLER_MAP[styleName];\n if (!handlers) {\n // Create a default handler for unknown styles (maps to kebab-case CSS or custom props)\n handlers = STYLE_HANDLER_MAP[styleName] = [createStyle(styleName)];\n }\n\n handlers.forEach((handler) => {\n if (!seenHandlers.has(handler)) {\n seenHandlers.add(handler);\n handlerQueue.push(handler);\n }\n });\n });\n\n // Accumulate declarations (ordered). We intentionally ignore `$` selector fan-out\n // and any responsive/state bindings for keyframes.\n const declarationPairs: { prop: string; value: string }[] = [];\n\n handlerQueue.forEach((handler) => {\n const lookup = handler.__lookupStyles;\n const filteredMap = lookup.reduce<StyleValueStateMap>(\n (acc, name) => {\n const v = styleMap[name];\n if (v !== undefined) acc[name] = v;\n return acc;\n },\n {},\n );\n\n const result = handler(filteredMap);\n if (!result) return;\n\n const results = Array.isArray(result) ? result : [result];\n results.forEach((cssMap) => {\n if (!cssMap || typeof cssMap !== 'object') return;\n const { $: _$, ...props } = cssMap as CSSMap;\n\n Object.entries(props).forEach(([prop, val]) => {\n if (val == null || val === '') return;\n\n if (Array.isArray(val)) {\n // Multiple values for the same property -> emit in order\n val.forEach((v) => {\n if (v != null && v !== '') {\n declarationPairs.push({ prop, value: String(v) });\n }\n });\n } else {\n declarationPairs.push({ prop, value: String(val) });\n }\n });\n });\n });\n\n // Fallback: if nothing produced (e.g., empty object), generate empty block\n const declarations = declarationPairs\n .map((d) => `${d.prop}: ${d.value}`)\n .join('; ');\n\n rules.push(`${key} { ${declarations.trim()} }`);\n }\n\n return rules.join(' ');\n }\n\n /**\n * Insert keyframes rule\n */\n insertKeyframes(\n registry: RootRegistry,\n steps: KeyframesSteps,\n name: string,\n root: Document | ShadowRoot,\n ): KeyframesInfo | null {\n let targetSheet = this.findAvailableSheet(registry, root);\n if (!targetSheet) {\n targetSheet = this.createSheet(registry, root);\n }\n\n const ruleIndex = this.findAvailableRuleIndex(targetSheet);\n const sheetIndex = registry.sheets.indexOf(targetSheet);\n\n try {\n const cssSteps = this.stepsToCSS(steps);\n const fullRule = `@keyframes ${name} { ${cssSteps} }`;\n\n const styleElement = targetSheet.sheet;\n const styleSheet = styleElement.sheet;\n\n if (styleSheet && !this.config.forceTextInjection) {\n const safeIndex = Math.min(\n Math.max(0, ruleIndex),\n styleSheet.cssRules.length,\n );\n styleSheet.insertRule(fullRule, safeIndex);\n } else {\n styleElement.textContent =\n (styleElement.textContent || '') + '\\n' + fullRule;\n }\n\n targetSheet.ruleCount++;\n\n return {\n name,\n ruleIndex,\n sheetIndex,\n cssText: this.config.devMode ? fullRule : undefined,\n };\n } catch (error) {\n console.warn('Failed to insert keyframes:', error);\n return null;\n }\n }\n\n /**\n * Delete keyframes rule\n */\n deleteKeyframes(registry: RootRegistry, info: KeyframesInfo): void {\n const sheet = registry.sheets[info.sheetIndex];\n if (!sheet) return;\n\n try {\n const styleElement = sheet.sheet;\n const styleSheet = styleElement.sheet;\n\n if (styleSheet) {\n if (\n info.ruleIndex >= 0 &&\n info.ruleIndex < styleSheet.cssRules.length\n ) {\n styleSheet.deleteRule(info.ruleIndex);\n sheet.ruleCount = Math.max(0, sheet.ruleCount - 1);\n\n // Adjust indices for all other rules in the same sheet\n // This is critical - when a keyframe rule is deleted, all rules\n // with higher indices shift down by 1\n this.adjustIndicesAfterDeletion(\n registry,\n info.sheetIndex,\n info.ruleIndex,\n info.ruleIndex,\n 1,\n // Create a dummy RuleInfo to satisfy the function signature\n {\n className: '',\n ruleIndex: info.ruleIndex,\n sheetIndex: info.sheetIndex,\n } as RuleInfo,\n [info.ruleIndex],\n );\n }\n }\n } catch (error) {\n console.warn('Failed to delete keyframes:', error);\n }\n }\n\n /**\n * Schedule async cleanup check (non-stacking)\n */\n public checkCleanupNeeded(registry: RootRegistry): void {\n // Clear any existing check timeout to prevent stacking\n if (registry.cleanupCheckTimeout) {\n clearTimeout(registry.cleanupCheckTimeout);\n registry.cleanupCheckTimeout = null;\n }\n\n // Schedule the actual check with setTimeout(..., 0)\n registry.cleanupCheckTimeout = setTimeout(() => {\n this.performCleanupCheck(registry);\n registry.cleanupCheckTimeout = null;\n }, 0);\n }\n\n /**\n * Perform the actual cleanup check (called asynchronously)\n */\n private performCleanupCheck(registry: RootRegistry): void {\n // Count unused rules (refCount = 0) - keyframes are disposed immediately\n const unusedRulesCount = Array.from(registry.refCounts.values()).filter(\n (count) => count === 0,\n ).length;\n const threshold = this.config.unusedStylesThreshold || 500;\n\n if (unusedRulesCount >= threshold) {\n this.scheduleBulkCleanup(registry);\n }\n }\n\n /**\n * Clean up resources for a root\n */\n cleanup(root: Document | ShadowRoot): void {\n const registry = this.rootRegistries.get(root);\n\n if (!registry) {\n return;\n }\n\n // Cancel any scheduled bulk cleanup\n if (registry.bulkCleanupTimeout) {\n if (\n this.config.idleCleanup &&\n typeof cancelIdleCallback !== 'undefined'\n ) {\n cancelIdleCallback(registry.bulkCleanupTimeout as unknown as number);\n } else {\n clearTimeout(registry.bulkCleanupTimeout);\n }\n registry.bulkCleanupTimeout = null;\n }\n\n // Cancel any scheduled cleanup check\n if (registry.cleanupCheckTimeout) {\n clearTimeout(registry.cleanupCheckTimeout);\n registry.cleanupCheckTimeout = null;\n }\n\n // Remove all sheets\n for (const sheet of registry.sheets) {\n try {\n // Remove style element\n const styleElement = sheet.sheet;\n if (styleElement.parentNode) {\n styleElement.parentNode.removeChild(styleElement);\n }\n } catch (error) {\n console.warn('Failed to cleanup sheet:', error);\n }\n }\n\n // Clear registry\n this.rootRegistries.delete(root);\n\n // Clean up raw CSS style element\n const rawStyleElement = this.rawStyleElements.get(root);\n if (rawStyleElement?.parentNode) {\n rawStyleElement.parentNode.removeChild(rawStyleElement);\n }\n this.rawStyleElements.delete(root);\n this.rawCSSBlocks.delete(root);\n }\n\n /**\n * Get or create a dedicated style element for raw CSS\n * Raw CSS is kept separate from tasty-managed sheets to avoid index conflicts\n */\n private getOrCreateRawStyleElement(\n root: Document | ShadowRoot,\n ): HTMLStyleElement {\n let styleElement = this.rawStyleElements.get(root);\n\n if (!styleElement) {\n styleElement =\n (root as Document).createElement?.('style') ||\n document.createElement('style');\n\n if (this.config.nonce) {\n styleElement.nonce = this.config.nonce;\n }\n\n styleElement.setAttribute('data-tasty-raw', '');\n\n // Append to head or shadow root\n if ('head' in root && root.head) {\n root.head.appendChild(styleElement);\n } else if ('appendChild' in root) {\n root.appendChild(styleElement);\n } else {\n document.head.appendChild(styleElement);\n }\n\n this.rawStyleElements.set(root, styleElement);\n this.rawCSSBlocks.set(root, new Map());\n }\n\n return styleElement;\n }\n\n /**\n * Inject raw CSS text directly without parsing\n * Returns a dispose function to remove the injected CSS\n */\n injectRawCSS(css: string, root: Document | ShadowRoot): RawCSSResult {\n if (!css.trim()) {\n return { dispose: () => { /* noop */ } };\n }\n\n const styleElement = this.getOrCreateRawStyleElement(root);\n const blocksMap = this.rawCSSBlocks.get(root)!;\n\n // Generate unique ID for this block\n const id = `raw_${this.rawCSSCounter++}`;\n\n // Calculate offsets\n const currentContent = styleElement.textContent || '';\n const startOffset = currentContent.length;\n const cssWithNewline = (currentContent ? '\\n' : '') + css;\n const endOffset = startOffset + cssWithNewline.length;\n\n // Append CSS\n styleElement.textContent = currentContent + cssWithNewline;\n\n // Track the block\n const info: RawCSSInfo = {\n id,\n css,\n startOffset,\n endOffset,\n };\n blocksMap.set(id, info);\n\n return {\n dispose: () => {\n this.disposeRawCSS(id, root);\n },\n };\n }\n\n /**\n * Remove a raw CSS block by ID\n */\n private disposeRawCSS(id: string, root: Document | ShadowRoot): void {\n const styleElement = this.rawStyleElements.get(root);\n const blocksMap = this.rawCSSBlocks.get(root);\n\n if (!styleElement || !blocksMap) {\n return;\n }\n\n const info = blocksMap.get(id);\n if (!info) {\n return;\n }\n\n // Remove from tracking\n blocksMap.delete(id);\n\n // Rebuild the CSS content from remaining blocks\n // This is simpler and more reliable than trying to splice strings\n const remainingBlocks = Array.from(blocksMap.values());\n\n if (remainingBlocks.length === 0) {\n styleElement.textContent = '';\n } else {\n // Rebuild with remaining CSS blocks in order of their original insertion\n // Sort by original startOffset to maintain order\n remainingBlocks.sort((a, b) => a.startOffset - b.startOffset);\n const newContent = remainingBlocks.map((block) => block.css).join('\\n');\n styleElement.textContent = newContent;\n\n // Update offsets for remaining blocks\n let offset = 0;\n for (const block of remainingBlocks) {\n block.startOffset = offset;\n block.endOffset = offset + block.css.length;\n offset = block.endOffset + 1; // +1 for newline\n }\n }\n }\n\n /**\n * Get the raw CSS content for SSR\n */\n getRawCSSText(root: Document | ShadowRoot): string {\n const styleElement = this.rawStyleElements.get(root);\n return styleElement?.textContent || '';\n }\n}\n"],"mappings":"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"}
@@ -1,18 +0,0 @@
1
- //#region src/injector/types.d.ts
2
- type KeyframeStep = string | Record<string, string | number>;
3
- type KeyframesSteps = Record<string, KeyframeStep>;
4
- /**
5
- * Definition for a CSS @property at-rule.
6
- * Used to define custom property syntax, inheritance, and initial value.
7
- */
8
- interface PropertyDefinition {
9
- /** CSS syntax string (e.g., '<color>', '<angle>', '<number>') */
10
- syntax?: string;
11
- /** Whether the property inherits (default: true) */
12
- inherits?: boolean;
13
- /** Initial value for the property */
14
- initialValue?: string | number;
15
- }
16
- //#endregion
17
- export { KeyframesSteps, PropertyDefinition };
18
- //# sourceMappingURL=types.d.mts.map
@@ -1,156 +0,0 @@
1
- //#region src/keyframes/index.ts
2
- const KEYFRAMES_KEY = "@keyframes";
3
- /**
4
- * Pattern to extract animation names from CSS animation property values.
5
- * Animation name is typically the first identifier in the shorthand.
6
- * Handles: "fadeIn 300ms ease-in", "pulse 1s infinite", etc.
7
- *
8
- * CSS animation shorthand order (all optional except name):
9
- * animation: name | duration | timing | delay | iteration | direction | fill-mode | play-state
10
- *
11
- * Animation names must:
12
- * - Start with a letter, underscore, or hyphen (but not a digit or CSS keyword)
13
- * - Not be CSS keywords: none, initial, inherit, unset, revert
14
- */
15
- const CSS_KEYWORDS = new Set([
16
- "none",
17
- "initial",
18
- "inherit",
19
- "unset",
20
- "revert",
21
- "auto",
22
- "normal",
23
- "running",
24
- "paused"
25
- ]);
26
- /**
27
- * Pattern to match animation name at the start of an animation value.
28
- * Must start with letter, underscore, or hyphen (not digit).
29
- */
30
- const ANIMATION_NAME_PATTERN = /^([a-zA-Z_-][a-zA-Z0-9_-]*)/;
31
- /**
32
- * Check if styles object has local @keyframes definition.
33
- * Fast path: single property lookup.
34
- */
35
- function hasLocalKeyframes(styles) {
36
- return KEYFRAMES_KEY in styles;
37
- }
38
- /**
39
- * Extract local @keyframes from styles object.
40
- * Returns null if no local keyframes (fast path).
41
- */
42
- function extractLocalKeyframes(styles) {
43
- const keyframes = styles[KEYFRAMES_KEY];
44
- if (!keyframes || typeof keyframes !== "object") return null;
45
- return keyframes;
46
- }
47
- /**
48
- * Merge local and global keyframes.
49
- * Local keyframes take priority over global.
50
- * Returns null if no keyframes exist (fast path).
51
- */
52
- function mergeKeyframes(local, global) {
53
- if (!local && !global) return null;
54
- if (!local) return global;
55
- if (!global) return local;
56
- return {
57
- ...global,
58
- ...local
59
- };
60
- }
61
- /**
62
- * Extract animation name from a single animation value.
63
- * Returns null if no valid name found.
64
- *
65
- * Examples:
66
- * - "fadeIn 300ms ease-in" → "fadeIn"
67
- * - "1s pulse infinite" → "pulse" (name can be anywhere)
68
- * - "none" → null (CSS keyword)
69
- * - "300ms ease-in" → null (no name, just duration/timing)
70
- */
71
- function extractAnimationNameFromValue(value) {
72
- const trimmed = value.trim();
73
- if (!trimmed) return null;
74
- const parts = trimmed.split(/\s+/);
75
- for (const part of parts) {
76
- if (CSS_KEYWORDS.has(part.toLowerCase())) continue;
77
- if (/^-?[\d.]+m?s$/i.test(part)) continue;
78
- if (part === "infinite" || /^\d+$/.test(part)) continue;
79
- if ([
80
- "normal",
81
- "reverse",
82
- "alternate",
83
- "alternate-reverse"
84
- ].includes(part.toLowerCase())) continue;
85
- if ([
86
- "forwards",
87
- "backwards",
88
- "both"
89
- ].includes(part.toLowerCase())) continue;
90
- if (["running", "paused"].includes(part.toLowerCase())) continue;
91
- if (/^(ease|linear|ease-in|ease-out|ease-in-out|step-start|step-end)$/i.test(part)) continue;
92
- if (/^(cubic-bezier|steps)\(/i.test(part)) continue;
93
- const match = ANIMATION_NAME_PATTERN.exec(part);
94
- if (match) return match[1];
95
- }
96
- return null;
97
- }
98
- /**
99
- * Extract all animation names from an animation property value.
100
- * Handles multiple animations separated by commas.
101
- *
102
- * Example: "fadeIn 300ms, slideIn 500ms ease-out" → ["fadeIn", "slideIn"]
103
- */
104
- function extractAnimationNamesFromAnimationValue(value) {
105
- const names = [];
106
- const animations = value.split(",");
107
- for (const animation of animations) {
108
- const name = extractAnimationNameFromValue(animation);
109
- if (name && !names.includes(name)) names.push(name);
110
- }
111
- return names;
112
- }
113
- /**
114
- * Extract animation names from a style value (handles mappings and arrays).
115
- */
116
- function extractAnimationNamesFromStyleValue(value, names) {
117
- if (typeof value === "string") for (const name of extractAnimationNamesFromAnimationValue(value)) names.add(name);
118
- else if (Array.isArray(value)) for (const v of value) extractAnimationNamesFromStyleValue(v, names);
119
- else if (value && typeof value === "object") for (const v of Object.values(value)) extractAnimationNamesFromStyleValue(v, names);
120
- }
121
- /**
122
- * Extract all animation names referenced in styles.
123
- * Scans 'animation' and 'animationName' properties including in state mappings.
124
- * Returns empty set if no animation properties found (fast path).
125
- */
126
- function extractAnimationNamesFromStyles(styles) {
127
- const names = /* @__PURE__ */ new Set();
128
- if ("animation" in styles) extractAnimationNamesFromStyleValue(styles.animation, names);
129
- if ("animationName" in styles) extractAnimationNamesFromStyleValue(styles.animationName, names);
130
- for (const [key, value] of Object.entries(styles)) {
131
- if (key === "$" || key === KEYFRAMES_KEY) continue;
132
- if ((key.startsWith("&") || key.startsWith(".") || /^[A-Z]/.test(key)) && value && typeof value === "object") {
133
- const nestedNames = extractAnimationNamesFromStyles(value);
134
- for (const name of nestedNames) names.add(name);
135
- }
136
- }
137
- return names;
138
- }
139
- /**
140
- * Filter keyframes to only those that are actually used.
141
- * Returns null if no keyframes are used (fast path).
142
- */
143
- function filterUsedKeyframes(keyframes, usedNames) {
144
- if (!keyframes || usedNames.size === 0) return null;
145
- const used = {};
146
- let hasAny = false;
147
- for (const name of usedNames) if (keyframes[name]) {
148
- used[name] = keyframes[name];
149
- hasAny = true;
150
- }
151
- return hasAny ? used : null;
152
- }
153
-
154
- //#endregion
155
- export { extractAnimationNamesFromStyles, extractLocalKeyframes, filterUsedKeyframes, hasLocalKeyframes, mergeKeyframes };
156
- //# sourceMappingURL=index.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/keyframes/index.ts"],"sourcesContent":["/**\n * Keyframes Utilities\n *\n * Optimized utilities for extracting and processing keyframes in styles.\n * Designed for zero overhead when no keyframes are used.\n */\n\nimport { getGlobalKeyframes, hasGlobalKeyframes } from '../config';\nimport type { KeyframesSteps } from '../injector/types';\nimport type { Styles } from '../styles/types';\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst KEYFRAMES_KEY = '@keyframes';\n\n/**\n * Pattern to extract animation names from CSS animation property values.\n * Animation name is typically the first identifier in the shorthand.\n * Handles: \"fadeIn 300ms ease-in\", \"pulse 1s infinite\", etc.\n *\n * CSS animation shorthand order (all optional except name):\n * animation: name | duration | timing | delay | iteration | direction | fill-mode | play-state\n *\n * Animation names must:\n * - Start with a letter, underscore, or hyphen (but not a digit or CSS keyword)\n * - Not be CSS keywords: none, initial, inherit, unset, revert\n */\nconst CSS_KEYWORDS = new Set([\n 'none',\n 'initial',\n 'inherit',\n 'unset',\n 'revert',\n 'auto',\n 'normal',\n 'running',\n 'paused',\n]);\n\n/**\n * Pattern to match animation name at the start of an animation value.\n * Must start with letter, underscore, or hyphen (not digit).\n */\nconst ANIMATION_NAME_PATTERN = /^([a-zA-Z_-][a-zA-Z0-9_-]*)/;\n\n// ============================================================================\n// Extraction Functions\n// ============================================================================\n\n/**\n * Check if styles object has local @keyframes definition.\n * Fast path: single property lookup.\n */\nexport function hasLocalKeyframes(styles: Styles): boolean {\n return KEYFRAMES_KEY in styles;\n}\n\n/**\n * Extract local @keyframes from styles object.\n * Returns null if no local keyframes (fast path).\n */\nexport function extractLocalKeyframes(\n styles: Styles,\n): Record<string, KeyframesSteps> | null {\n const keyframes = styles[KEYFRAMES_KEY];\n if (!keyframes || typeof keyframes !== 'object') {\n return null;\n }\n return keyframes as Record<string, KeyframesSteps>;\n}\n\n/**\n * Merge local and global keyframes.\n * Local keyframes take priority over global.\n * Returns null if no keyframes exist (fast path).\n */\nexport function mergeKeyframes(\n local: Record<string, KeyframesSteps> | null,\n global: Record<string, KeyframesSteps> | null,\n): Record<string, KeyframesSteps> | null {\n if (!local && !global) return null;\n if (!local) return global;\n if (!global) return local;\n // Local overrides global\n return { ...global, ...local };\n}\n\n/**\n * Get merged keyframes for styles (local + global).\n * Returns null if no keyframes defined anywhere (fast path).\n */\nexport function getKeyframesForStyles(\n styles: Styles,\n): Record<string, KeyframesSteps> | null {\n const local = extractLocalKeyframes(styles);\n const global = hasGlobalKeyframes() ? getGlobalKeyframes() : null;\n return mergeKeyframes(local, global);\n}\n\n// ============================================================================\n// Animation Name Extraction\n// ============================================================================\n\n/**\n * Extract animation name from a single animation value.\n * Returns null if no valid name found.\n *\n * Examples:\n * - \"fadeIn 300ms ease-in\" → \"fadeIn\"\n * - \"1s pulse infinite\" → \"pulse\" (name can be anywhere)\n * - \"none\" → null (CSS keyword)\n * - \"300ms ease-in\" → null (no name, just duration/timing)\n */\nfunction extractAnimationNameFromValue(value: string): string | null {\n const trimmed = value.trim();\n if (!trimmed) return null;\n\n // Split by whitespace and find the first valid animation name\n const parts = trimmed.split(/\\s+/);\n\n for (const part of parts) {\n // Skip CSS keywords\n if (CSS_KEYWORDS.has(part.toLowerCase())) continue;\n\n // Skip time values (e.g., 300ms, 1s, 0.5s)\n if (/^-?[\\d.]+m?s$/i.test(part)) continue;\n\n // Skip iteration counts (e.g., infinite, 3)\n if (part === 'infinite' || /^\\d+$/.test(part)) continue;\n\n // Skip direction values\n if (\n ['normal', 'reverse', 'alternate', 'alternate-reverse'].includes(\n part.toLowerCase(),\n )\n )\n continue;\n\n // Skip fill-mode values\n if (['forwards', 'backwards', 'both'].includes(part.toLowerCase()))\n continue;\n\n // Skip play-state values\n if (['running', 'paused'].includes(part.toLowerCase())) continue;\n\n // Skip timing functions (ease, linear, ease-in, etc., or cubic-bezier/steps)\n if (\n /^(ease|linear|ease-in|ease-out|ease-in-out|step-start|step-end)$/i.test(\n part,\n )\n )\n continue;\n if (/^(cubic-bezier|steps)\\(/i.test(part)) continue;\n\n // Check if it looks like a valid animation name\n const match = ANIMATION_NAME_PATTERN.exec(part);\n if (match) {\n return match[1];\n }\n }\n\n return null;\n}\n\n/**\n * Extract all animation names from an animation property value.\n * Handles multiple animations separated by commas.\n *\n * Example: \"fadeIn 300ms, slideIn 500ms ease-out\" → [\"fadeIn\", \"slideIn\"]\n */\nfunction extractAnimationNamesFromAnimationValue(value: string): string[] {\n const names: string[] = [];\n\n // Split by comma for multiple animations\n const animations = value.split(',');\n\n for (const animation of animations) {\n const name = extractAnimationNameFromValue(animation);\n if (name && !names.includes(name)) {\n names.push(name);\n }\n }\n\n return names;\n}\n\n/**\n * Extract animation names from a style value (handles mappings and arrays).\n */\nfunction extractAnimationNamesFromStyleValue(\n value: unknown,\n names: Set<string>,\n): void {\n if (typeof value === 'string') {\n for (const name of extractAnimationNamesFromAnimationValue(value)) {\n names.add(name);\n }\n } else if (Array.isArray(value)) {\n // Responsive array\n for (const v of value) {\n extractAnimationNamesFromStyleValue(v, names);\n }\n } else if (value && typeof value === 'object') {\n // State mapping\n for (const v of Object.values(value)) {\n extractAnimationNamesFromStyleValue(v, names);\n }\n }\n}\n\n/**\n * Extract all animation names referenced in styles.\n * Scans 'animation' and 'animationName' properties including in state mappings.\n * Returns empty set if no animation properties found (fast path).\n */\nexport function extractAnimationNamesFromStyles(styles: Styles): Set<string> {\n const names = new Set<string>();\n\n // Check animation property\n if ('animation' in styles) {\n extractAnimationNamesFromStyleValue(styles.animation, names);\n }\n\n // Check animationName property\n if ('animationName' in styles) {\n extractAnimationNamesFromStyleValue(styles.animationName, names);\n }\n\n // Check nested selectors (sub-elements)\n for (const [key, value] of Object.entries(styles)) {\n // Skip non-selector keys and special keys\n if (key === '$' || key === KEYFRAMES_KEY) continue;\n\n // Check if it's a selector (starts with &, ., or uppercase)\n if (\n (key.startsWith('&') || key.startsWith('.') || /^[A-Z]/.test(key)) &&\n value &&\n typeof value === 'object'\n ) {\n // Recursively extract from nested styles\n const nestedNames = extractAnimationNamesFromStyles(value as Styles);\n for (const name of nestedNames) {\n names.add(name);\n }\n }\n }\n\n return names;\n}\n\n// ============================================================================\n// Name Replacement\n// ============================================================================\n\n/**\n * Replace animation names in CSS declarations with injected names.\n * Optimized to avoid regex creation - uses simple string replacement.\n *\n * @param declarations CSS declarations string\n * @param nameMap Map from original name to injected name (only contains names that differ)\n * @returns Updated declarations string\n */\nexport function replaceAnimationNames(\n declarations: string,\n nameMap: Map<string, string>,\n): string {\n // Fast path: no animation properties\n if (!declarations.includes('animation')) return declarations;\n\n // Parse and replace\n const parts = declarations.split(';');\n let modified = false;\n\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i];\n const colonIdx = part.indexOf(':');\n if (colonIdx === -1) continue;\n\n const prop = part.slice(0, colonIdx).trim().toLowerCase();\n\n if (prop === 'animation' || prop === 'animation-name') {\n const prefix = part.slice(0, colonIdx + 1);\n let value = part.slice(colonIdx + 1);\n\n // Replace each animation name using simple word replacement\n for (const [original, injected] of nameMap) {\n // Simple word boundary replacement without regex\n const newValue = replaceWord(value, original, injected);\n if (newValue !== value) {\n value = newValue;\n modified = true;\n }\n }\n\n parts[i] = prefix + value;\n }\n }\n\n return modified ? parts.join(';') : declarations;\n}\n\n/**\n * Replace a word in a string (word boundary aware, no regex).\n */\nfunction replaceWord(str: string, word: string, replacement: string): string {\n let result = str;\n let idx = 0;\n\n while ((idx = result.indexOf(word, idx)) !== -1) {\n // Check word boundaries\n const before = idx === 0 ? ' ' : result[idx - 1];\n const after =\n idx + word.length >= result.length ? ' ' : result[idx + word.length];\n\n const isWordBoundaryBefore = !/[a-zA-Z0-9_-]/.test(before);\n const isWordBoundaryAfter = !/[a-zA-Z0-9_-]/.test(after);\n\n if (isWordBoundaryBefore && isWordBoundaryAfter) {\n result =\n result.slice(0, idx) + replacement + result.slice(idx + word.length);\n idx += replacement.length;\n } else {\n idx += word.length;\n }\n }\n\n return result;\n}\n\n// ============================================================================\n// Filter Functions\n// ============================================================================\n\n/**\n * Filter keyframes to only those that are actually used.\n * Returns null if no keyframes are used (fast path).\n */\nexport function filterUsedKeyframes(\n keyframes: Record<string, KeyframesSteps> | null,\n usedNames: Set<string>,\n): Record<string, KeyframesSteps> | null {\n if (!keyframes || usedNames.size === 0) return null;\n\n const used: Record<string, KeyframesSteps> = {};\n let hasAny = false;\n\n for (const name of usedNames) {\n if (keyframes[name]) {\n used[name] = keyframes[name];\n hasAny = true;\n }\n }\n\n return hasAny ? used : null;\n}\n"],"mappings":"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"}
@@ -1,319 +0,0 @@
1
- import { COLOR_FUNCS, RE_HEX, RE_NUMBER, RE_RAW_UNIT, RE_UNIT_NUM, VALUE_KEYWORDS } from "./const.mjs";
2
- import { Bucket } from "./types.mjs";
3
- import { StyleParser } from "./parser.mjs";
4
- import { getGlobalPredefinedTokens } from "../utils/styles.mjs";
5
-
6
- //#region src/parser/classify.ts
7
- /**
8
- * Re-parses a value through the parser until it stabilizes (no changes)
9
- * or max iterations reached. This allows units to reference other units.
10
- * Example: { x: '8px', y: '2x' } -> '1y' resolves to '16px'
11
- */
12
- function resolveUntilStable(value, opts, recurse, maxIterations = 10) {
13
- let current = value;
14
- for (let i = 0; i < maxIterations; i++) {
15
- const unitMatch = current.match(RE_UNIT_NUM);
16
- if (!unitMatch) break;
17
- const unitName = unitMatch[1];
18
- if (!opts.units || !(unitName in opts.units)) break;
19
- const result = recurse(current);
20
- if (result.output === current) break;
21
- current = result.output;
22
- }
23
- return current;
24
- }
25
- function classify(raw, opts, recurse) {
26
- const token = raw.trim();
27
- if (!token) return {
28
- bucket: Bucket.Mod,
29
- processed: ""
30
- };
31
- {
32
- let depth = 0;
33
- let inQuote = 0;
34
- for (let i = 0; i < token.length; i++) {
35
- const ch = token[i];
36
- if (inQuote) {
37
- if (ch === inQuote && token[i - 1] !== "\\") inQuote = 0;
38
- continue;
39
- }
40
- if (ch === "\"" || ch === "'") {
41
- inQuote = ch;
42
- continue;
43
- }
44
- if (ch === "(") depth++;
45
- else if (ch === ")") depth = Math.max(0, depth - 1);
46
- }
47
- if (depth !== 0) {
48
- console.warn("tasty: skipped invalid function token with unmatched parentheses:", token);
49
- return {
50
- bucket: Bucket.Mod,
51
- processed: ""
52
- };
53
- }
54
- }
55
- if (token.startsWith("\"") && token.endsWith("\"") || token.startsWith("'") && token.endsWith("'")) return {
56
- bucket: Bucket.Value,
57
- processed: token
58
- };
59
- if (token.startsWith("$$")) {
60
- const name = token.slice(2);
61
- if (/^[a-z_][a-z0-9-_]*$/i.test(name)) return {
62
- bucket: Bucket.Value,
63
- processed: `--${name}`
64
- };
65
- }
66
- if (token.startsWith("##")) {
67
- const name = token.slice(2);
68
- if (/^[a-z_][a-z0-9-_]*$/i.test(name)) return {
69
- bucket: Bucket.Value,
70
- processed: `--${name}-color`
71
- };
72
- }
73
- if (token === "#current") return {
74
- bucket: Bucket.Color,
75
- processed: "currentcolor"
76
- };
77
- const currentAlphaMatch = token.match(/^#current\.(\$[a-z_][a-z0-9-_]*|[0-9]+)$/i);
78
- if (currentAlphaMatch) {
79
- const rawAlpha = currentAlphaMatch[1];
80
- let percentage;
81
- if (rawAlpha.startsWith("$")) percentage = `calc(var(--${rawAlpha.slice(1)}) * 100%)`;
82
- else if (rawAlpha === "0") percentage = "0%";
83
- else percentage = `${parseFloat("." + rawAlpha) * 100}%`;
84
- return {
85
- bucket: Bucket.Color,
86
- processed: `color-mix(in oklab, currentcolor ${percentage}, transparent)`
87
- };
88
- }
89
- if (token[0] === "$" || token[0] === "#") {
90
- const predefinedTokens = getGlobalPredefinedTokens();
91
- if (predefinedTokens) {
92
- if (token in predefinedTokens) {
93
- const tokenValue = predefinedTokens[token];
94
- return classify(tokenValue.toLowerCase(), opts, recurse);
95
- }
96
- if (token[0] === "#") {
97
- const alphaMatch = token.match(/^(#[a-z0-9-]+)\.(\$[a-z_][a-z0-9-_]*|[0-9]+)$/i);
98
- if (alphaMatch) {
99
- const [, baseToken, rawAlpha] = alphaMatch;
100
- if (baseToken in predefinedTokens) {
101
- const resolvedValue = predefinedTokens[baseToken];
102
- if (resolvedValue.startsWith("#")) return classify(`${resolvedValue.toLowerCase()}.${rawAlpha}`, opts, recurse);
103
- const funcMatch = resolvedValue.match(/^(rgba?|hsla?|hwb|oklab|oklch|lab|lch|color|okhsl|device-cmyk|gray|color-mix|color-contrast)\((.+)\)$/i);
104
- if (funcMatch) {
105
- const [, funcName, args] = funcMatch;
106
- let alpha;
107
- if (rawAlpha.startsWith("$")) alpha = `var(--${rawAlpha.slice(1)})`;
108
- else alpha = rawAlpha === "0" ? "0" : `.${rawAlpha}`;
109
- const normalizedFunc = funcName.replace(/a$/i, "").toLowerCase();
110
- const normalizeArgs = (a) => {
111
- let result = "";
112
- let depth = 0;
113
- for (let i = 0; i < a.length; i++) {
114
- const c = a[i];
115
- if (c === "(") {
116
- depth++;
117
- result += c;
118
- } else if (c === ")") {
119
- depth = Math.max(0, depth - 1);
120
- result += c;
121
- } else if (c === "," && depth === 0) {
122
- while (i + 1 < a.length && /\s/.test(a[i + 1])) i++;
123
- result += " ";
124
- } else result += c;
125
- }
126
- return result;
127
- };
128
- const findLastTopLevel = (str, ch) => {
129
- let depth = 0;
130
- for (let i = str.length - 1; i >= 0; i--) {
131
- const c = str[i];
132
- if (c === ")") depth++;
133
- else if (c === "(") depth = Math.max(0, depth - 1);
134
- else if (c === ch && depth === 0) return i;
135
- }
136
- return -1;
137
- };
138
- const slashIdx = findLastTopLevel(args, "/");
139
- const hasModernAlpha = slashIdx !== -1;
140
- let topLevelCommaCount = 0;
141
- let lastTopLevelComma = -1;
142
- {
143
- let depth = 0;
144
- for (let i = 0; i < args.length; i++) {
145
- const c = args[i];
146
- if (c === "(") depth++;
147
- else if (c === ")") depth = Math.max(0, depth - 1);
148
- else if (c === "," && depth === 0) {
149
- topLevelCommaCount++;
150
- lastTopLevelComma = i;
151
- }
152
- }
153
- }
154
- const hasLegacyAlpha = !hasModernAlpha && /a$/i.test(funcName) && topLevelCommaCount === 3;
155
- const constructed = `${normalizedFunc}(${hasModernAlpha || hasLegacyAlpha ? normalizeArgs(hasModernAlpha ? args.slice(0, slashIdx).trim() : args.slice(0, lastTopLevelComma).trim()) : normalizeArgs(args)} / ${alpha})`;
156
- if (!COLOR_FUNCS.has(normalizedFunc) && opts.funcs && normalizedFunc in opts.funcs) return classify(constructed, opts, recurse);
157
- return {
158
- bucket: Bucket.Color,
159
- processed: constructed
160
- };
161
- }
162
- return classify(`${resolvedValue}.${rawAlpha}`, opts, recurse);
163
- }
164
- }
165
- }
166
- }
167
- }
168
- if (token.match(/^var\(--([a-z0-9-]+)-color\)$/)) return {
169
- bucket: Bucket.Color,
170
- processed: token
171
- };
172
- if (token.startsWith("url(")) return {
173
- bucket: Bucket.Value,
174
- processed: token
175
- };
176
- if (token[0] === "$") {
177
- const identMatch = token.match(/^\$([a-z_][a-z0-9-_]*)$/);
178
- if (identMatch) {
179
- const name = identMatch[1];
180
- const processed = `var(--${name})`;
181
- return {
182
- bucket: name.endsWith("-color") ? Bucket.Color : Bucket.Value,
183
- processed
184
- };
185
- }
186
- }
187
- if (token[0] === "#" && token.length > 1) {
188
- const alphaMatch = token.match(/^#([a-z0-9-]+)\.(\$[a-z_][a-z0-9-_]*|[0-9]+)$/i);
189
- if (alphaMatch) {
190
- const [, base, rawAlpha] = alphaMatch;
191
- let alpha;
192
- if (rawAlpha.startsWith("$")) alpha = `var(--${rawAlpha.slice(1)})`;
193
- else if (rawAlpha === "0") alpha = "0";
194
- else alpha = `.${rawAlpha}`;
195
- return {
196
- bucket: Bucket.Color,
197
- processed: `rgb(var(--${base}-color-rgb) / ${alpha})`
198
- };
199
- }
200
- const name = token.slice(1);
201
- if (RE_HEX.test(name)) return {
202
- bucket: Bucket.Color,
203
- processed: `var(--${name}-color, #${name})`
204
- };
205
- return {
206
- bucket: Bucket.Color,
207
- processed: `var(--${name}-color)`
208
- };
209
- }
210
- const openIdx = token.indexOf("(");
211
- if (openIdx > 0 && token.endsWith(")")) {
212
- const fname = token.slice(0, openIdx);
213
- const inner = token.slice(openIdx + 1, -1);
214
- if (COLOR_FUNCS.has(fname)) {
215
- const argProcessed = recurse(inner).output.replace(/,\s+/g, ",");
216
- return {
217
- bucket: Bucket.Color,
218
- processed: `${fname}(${argProcessed})`
219
- };
220
- }
221
- if (opts.funcs && fname in opts.funcs) {
222
- const tmp = new StyleParser(opts).process(inner);
223
- return classify(opts.funcs[fname](tmp.groups), {
224
- ...opts,
225
- funcs: void 0
226
- }, recurse);
227
- }
228
- const argProcessed = recurse(inner).output;
229
- return {
230
- bucket: Bucket.Value,
231
- processed: `${fname}(${argProcessed})`
232
- };
233
- }
234
- if (token.startsWith("(") && token.endsWith(")")) {
235
- const colorMatch = token.slice(1, -1).match(/^#([a-z0-9-]+)\s*,\s*(.*)$/i);
236
- if (colorMatch) {
237
- const [, name, fallback] = colorMatch;
238
- const processedFallback = recurse(fallback).output;
239
- return {
240
- bucket: Bucket.Color,
241
- processed: `var(--${name}-color, ${processedFallback})`
242
- };
243
- }
244
- }
245
- if (token.startsWith("(") && token.endsWith(")")) {
246
- const match = token.slice(1, -1).match(/^\$([a-z_][a-z0-9-_]*)\s*,\s*(.*)$/);
247
- if (match) {
248
- const [, name, fallback] = match;
249
- const processedFallback = recurse(fallback).output;
250
- return {
251
- bucket: name.endsWith("-color") ? Bucket.Color : Bucket.Value,
252
- processed: `var(--${name}, ${processedFallback})`
253
- };
254
- }
255
- }
256
- if (token[0] === "(" && token[token.length - 1] === ")") {
257
- const innerProcessed = recurse(token.slice(1, -1)).output;
258
- return {
259
- bucket: Bucket.Value,
260
- processed: `calc(${innerProcessed})`
261
- };
262
- }
263
- const um = token.match(RE_UNIT_NUM);
264
- if (um) {
265
- const unit = um[1];
266
- const numericPart = parseFloat(token.slice(0, -unit.length));
267
- const handler = opts.units && opts.units[unit];
268
- if (handler) if (typeof handler === "string") {
269
- const rawMatch = handler.match(RE_RAW_UNIT);
270
- if (rawMatch) {
271
- const [, baseNum, cssUnit] = rawMatch;
272
- const resolved = resolveUntilStable(`${numericPart * parseFloat(baseNum)}${cssUnit}`, opts, recurse);
273
- return {
274
- bucket: Bucket.Value,
275
- processed: resolved
276
- };
277
- }
278
- const base = handler;
279
- if (numericPart === 1) return {
280
- bucket: Bucket.Value,
281
- processed: base
282
- };
283
- return {
284
- bucket: Bucket.Value,
285
- processed: `calc(${numericPart} * ${base})`
286
- };
287
- } else {
288
- const inner = handler(numericPart);
289
- return {
290
- bucket: Bucket.Value,
291
- processed: inner
292
- };
293
- }
294
- }
295
- if (/^[+-]?(?:\d*\.\d+|\d+)[a-z%]+$/.test(token)) return {
296
- bucket: Bucket.Value,
297
- processed: token
298
- };
299
- if (RE_NUMBER.test(token)) return {
300
- bucket: Bucket.Value,
301
- processed: token
302
- };
303
- if (VALUE_KEYWORDS.has(token)) return {
304
- bucket: Bucket.Value,
305
- processed: token
306
- };
307
- if (token === "transparent" || token === "currentcolor") return {
308
- bucket: Bucket.Color,
309
- processed: token
310
- };
311
- return {
312
- bucket: Bucket.Mod,
313
- processed: token
314
- };
315
- }
316
-
317
- //#endregion
318
- export { classify };
319
- //# sourceMappingURL=classify.mjs.map