@@ -1318,6 +1318,8 @@ export class CSSGenerator {
13181318 private screenBreakpoints : Map < string , string >
13191319 // Cache for utility+value combinations that don't match any rule (negative cache)
13201320 private noMatchCache : Set < string > = new Set ( )
1321+ // When generating a shortcut, holds the shortcut name for selector building in addRule
1322+ private selectorAlias : string | null = null
13211323 // Preserve extend colors for CSS variable generation (only custom colors, not defaults)
13221324 private extendColors : Record < string , string | Record < string , string > > | null = null
13231325
@@ -1389,29 +1391,39 @@ export class CSSGenerator {
13891391 this . classCache . add ( className )
13901392 const classes = Array . isArray ( shortcut ) ? shortcut : shortcut . split ( / \s + / )
13911393 for ( const cls of classes ) {
1392- this . generate ( cls )
1394+ const cacheKey = `shortcut::${ className } ::${ cls } `
1395+ if ( ! this . classCache . has ( cacheKey ) ) {
1396+ this . classCache . add ( cacheKey )
1397+ this . selectorAlias = className
1398+ this . generateCore ( parseClass ( cls ) )
1399+ this . selectorAlias = null
1400+ }
13931401 }
13941402 return
13951403 }
13961404
13971405 this . classCache . add ( className )
1406+ this . generateCore ( parseClass ( className ) )
1407+ }
13981408
1409+ /**
1410+ * Core CSS generation logic for a parsed utility class
1411+ */
1412+ private generateCore ( parsed : ParsedClass ) : void {
13991413 // Check exact match blocklist first (O(1) Set lookup)
1400- if ( this . blocklistExact . size > 0 && this . blocklistExact . has ( className ) ) {
1414+ if ( this . blocklistExact . size > 0 && this . blocklistExact . has ( parsed . raw ) ) {
14011415 return
14021416 }
14031417
14041418 // Check if class is blocklisted (use pre-compiled regexes)
14051419 if ( this . blocklistRegexCache . length > 0 ) {
14061420 for ( let i = 0 ; i < this . blocklistRegexCache . length ; i ++ ) {
1407- if ( this . blocklistRegexCache [ i ] . test ( className ) ) {
1421+ if ( this . blocklistRegexCache [ i ] . test ( parsed . raw ) ) {
14081422 return
14091423 }
14101424 }
14111425 }
14121426
1413- const parsed = parseClass ( className )
1414-
14151427 // ==========================================================================
14161428 // FAST PATH: Static utility map lookup (O(1))
14171429 // Handles ~80% of common utilities without any rule iteration
@@ -1826,7 +1838,7 @@ export class CSSGenerator {
18261838 // Try custom rules from config first (allows overriding built-in rules)
18271839 if ( this . config . rules . length > 0 ) {
18281840 for ( const [ pattern , handler ] of this . config . rules ) {
1829- const match = className . match ( pattern )
1841+ const match = parsed . raw . match ( pattern )
18301842 if ( match ) {
18311843 const properties = handler ( match )
18321844 if ( properties ) {
@@ -1861,11 +1873,14 @@ export class CSSGenerator {
18611873 * Add a CSS rule with variants applied
18621874 */
18631875 private addRule ( parsed : ParsedClass , properties : Record < string , string > , childSelector ?: string , pseudoElement ?: string ) : void {
1864- // Use cached selector if available
1865- const cacheKey = `${ parsed . raw } ${ childSelector || '' } ${ pseudoElement || '' } `
1876+ // When generating shortcuts, use the shortcut name for the CSS selector
1877+ const effectiveParsed = this . selectorAlias ? { ...parsed , raw : this . selectorAlias } : parsed
1878+ const cacheKey = this . selectorAlias
1879+ ? `shortcut:${ parsed . raw } :${ this . selectorAlias } ${ childSelector || '' } ${ pseudoElement || '' } `
1880+ : `${ parsed . raw } ${ childSelector || '' } ${ pseudoElement || '' } `
18661881 let selector = this . selectorCache . get ( cacheKey )
18671882 if ( ! selector ) {
1868- selector = this . buildSelector ( parsed )
1883+ selector = this . buildSelector ( effectiveParsed )
18691884 // Append pseudo-element directly (no space)
18701885 if ( pseudoElement ) {
18711886 selector += pseudoElement
@@ -1877,10 +1892,10 @@ export class CSSGenerator {
18771892 this . selectorCache . set ( cacheKey , selector )
18781893 }
18791894
1880- const mediaQuery = this . getMediaQuery ( parsed )
1895+ const mediaQuery = this . getMediaQuery ( effectiveParsed )
18811896
18821897 // Apply !important modifier
1883- if ( parsed . important ) {
1898+ if ( effectiveParsed . important ) {
18841899 for ( const key in properties ) {
18851900 properties [ key ] += ' !important'
18861901 }
0 commit comments