@@ -7,6 +7,7 @@ import { Lru } from '../parser/lru';
77import { createStyle , STYLE_HANDLER_MAP } from '../styles' ;
88import { Styles } from '../styles/types' ;
99
10+ import { getModCombinationsIterative } from './getModCombinations' ;
1011import {
1112 mediaWrapper ,
1213 normalizeStyleZones ,
@@ -200,6 +201,17 @@ function hasConflictingAttributeSelectors(
200201 return false ;
201202}
202203
204+ /**
205+ * Create a conflict checker function that uses precomputed attribute maps
206+ * for efficient conflict detection during combination generation
207+ */
208+ function createAttributeConflictChecker (
209+ parsedMods : Map < string , ParsedAttributeSelector | null > ,
210+ ) : ( combination : string [ ] ) => boolean {
211+ return ( combination : string [ ] ) =>
212+ hasConflictingAttributeSelectors ( combination , parsedMods ) ;
213+ }
214+
203215// Interface for precomputed attribute maps to optimize not selector generation
204216interface AttributeMaps {
205217 allAttributes : Map < string , Set < string > > ;
@@ -372,10 +384,34 @@ function explodeHandlerResult(
372384 )
373385 : [ selectorSuffix ] ;
374386
375- // Create logical rules for each breakpoint × selector combination
376- for ( const [ breakpointIdx , declarations ] of breakpointGroups ) {
387+ // Early identical-breakpoint coalescing: skip duplicate declarations
388+ const seenDeclarations = new Map < string , number > ( ) ;
389+
390+ // Process breakpoints in order to prefer lower breakpoint indices
391+ const sortedBreakpoints = Array . from ( breakpointGroups . entries ( ) ) . sort (
392+ ( [ a ] , [ b ] ) => a - b ,
393+ ) ;
394+
395+ for ( const [ breakpointIdx , declarations ] of sortedBreakpoints ) {
377396 if ( Object . keys ( declarations ) . length === 0 ) continue ;
378397
398+ // Create a stable hash key for identical declarations
399+ const declarationKeys = Object . keys ( declarations ) . sort ( ) ;
400+ const declarationHash = declarationKeys
401+ . map ( ( key ) => `${ key } :${ declarations [ key ] } ` )
402+ . join ( ';' ) ;
403+
404+ const existingBreakpointIdx = seenDeclarations . get ( declarationHash ) ;
405+ if ( existingBreakpointIdx !== undefined ) {
406+ // Skip this breakpoint as it has identical declarations to a previous one
407+ // The CSS cascade will handle the responsive behavior correctly
408+ continue ;
409+ }
410+
411+ // Mark this declaration set as seen
412+ seenDeclarations . set ( declarationHash , breakpointIdx ) ;
413+
414+ // Create logical rules for this unique declaration set
379415 for ( const suffix of suffixes ) {
380416 logicalRules . push ( {
381417 selectorSuffix : suffix ,
@@ -626,21 +662,15 @@ export function renderStyles(
626662 // Precompute attribute maps once for all combinations
627663 const attributeMaps = buildAttributeMaps ( [ ] , allModsArray ) ;
628664
629- const combinations : string [ ] [ ] = [ [ ] ] ;
630- for ( let i = 0 ; i < allModsArray . length ; i ++ ) {
631- const currentLength = combinations . length ;
632- for ( let j = 0 ; j < currentLength ; j ++ ) {
633- const newCombination = [ ...combinations [ j ] , allModsArray [ i ] ] ;
634- if (
635- ! hasConflictingAttributeSelectors (
636- newCombination ,
637- attributeMaps . parsedMods ,
638- )
639- ) {
640- combinations . push ( newCombination ) ;
641- }
642- }
643- }
665+ // Generate combinations with conflict-aware pruning
666+ const conflictChecker = createAttributeConflictChecker (
667+ attributeMaps . parsedMods ,
668+ ) ;
669+ const combinations = getModCombinationsIterative (
670+ allModsArray ,
671+ true ,
672+ conflictChecker ,
673+ ) ;
644674
645675 combinations . forEach ( ( modCombination ) => {
646676 const stateProps : Record < string , any > = { } ;
@@ -734,24 +764,15 @@ export function renderStyles(
734764 // Precompute attribute maps once for all combinations
735765 const attributeMaps = buildAttributeMaps ( [ ] , allModsArray ) ;
736766
737- const combinations : string [ ] [ ] = [ [ ] ] ; // Start with empty combination
738-
739- // Generate all combinations (including empty)
740- for ( let i = 0 ; i < allModsArray . length ; i ++ ) {
741- const currentLength = combinations . length ;
742- for ( let j = 0 ; j < currentLength ; j ++ ) {
743- const newCombination = [ ...combinations [ j ] , allModsArray [ i ] ] ;
744- // Skip combinations with conflicting attribute selectors
745- if (
746- ! hasConflictingAttributeSelectors (
747- newCombination ,
748- attributeMaps . parsedMods ,
749- )
750- ) {
751- combinations . push ( newCombination ) ;
752- }
753- }
754- }
767+ // Generate combinations with conflict-aware pruning
768+ const conflictChecker = createAttributeConflictChecker (
769+ attributeMaps . parsedMods ,
770+ ) ;
771+ const combinations = getModCombinationsIterative (
772+ allModsArray ,
773+ true ,
774+ conflictChecker ,
775+ ) ;
755776
756777 combinations . forEach ( ( modCombination ) => {
757778 const stateProps : Record < string , any > = { } ;
@@ -1016,21 +1037,15 @@ export function renderStylesForGlobal(
10161037 // Precompute attribute maps once for all combinations
10171038 const attributeMaps = buildAttributeMaps ( [ ] , allModsArray ) ;
10181039
1019- const combinations : string [ ] [ ] = [ [ ] ] ;
1020- for ( let a = 0 ; a < allModsArray . length ; a ++ ) {
1021- const currentLength = combinations . length ;
1022- for ( let b = 0 ; b < currentLength ; b ++ ) {
1023- const newCombination = [ ...combinations [ b ] , allModsArray [ a ] ] ;
1024- if (
1025- ! hasConflictingAttributeSelectors (
1026- newCombination ,
1027- attributeMaps . parsedMods ,
1028- )
1029- ) {
1030- combinations . push ( newCombination ) ;
1031- }
1032- }
1033- }
1040+ // Generate combinations with conflict-aware pruning
1041+ const conflictChecker = createAttributeConflictChecker (
1042+ attributeMaps . parsedMods ,
1043+ ) ;
1044+ const combinations = getModCombinationsIterative (
1045+ allModsArray ,
1046+ true ,
1047+ conflictChecker ,
1048+ ) ;
10341049
10351050 combinations . forEach ( ( modCombination ) => {
10361051 const stateProps : Record < string , any > = { } ;
@@ -1115,24 +1130,15 @@ export function renderStylesForGlobal(
11151130 // Precompute attribute maps once for all combinations
11161131 const attributeMaps = buildAttributeMaps ( [ ] , allModsArray ) ;
11171132
1118- const combinations : string [ ] [ ] = [ [ ] ] ; // Start with empty combination
1119-
1120- // Generate all combinations (including empty)
1121- for ( let i = 0 ; i < allModsArray . length ; i ++ ) {
1122- const currentLength = combinations . length ;
1123- for ( let j = 0 ; j < currentLength ; j ++ ) {
1124- const newCombination = [ ...combinations [ j ] , allModsArray [ i ] ] ;
1125- // Skip combinations with conflicting attribute selectors
1126- if (
1127- ! hasConflictingAttributeSelectors (
1128- newCombination ,
1129- attributeMaps . parsedMods ,
1130- )
1131- ) {
1132- combinations . push ( newCombination ) ;
1133- }
1134- }
1135- }
1133+ // Generate combinations with conflict-aware pruning
1134+ const conflictChecker = createAttributeConflictChecker (
1135+ attributeMaps . parsedMods ,
1136+ ) ;
1137+ const combinations = getModCombinationsIterative (
1138+ allModsArray ,
1139+ true ,
1140+ conflictChecker ,
1141+ ) ;
11361142
11371143 combinations . forEach ( ( modCombination ) => {
11381144 const stateProps : Record < string , any > = { } ;
0 commit comments