@@ -17,6 +17,7 @@ import {
17
17
MultiMap ,
18
18
ObjectDictionary ,
19
19
objectValues ,
20
+ assertNever ,
20
21
} from "@opticss/util" ;
21
22
22
23
import {
@@ -169,6 +170,13 @@ export interface SerializedElementAnalysis {
169
170
dynamicAttributes : Array < SerializedDynamicAttrs > ;
170
171
}
171
172
173
+ type AnalyzedStyle < BooleanExpression , StringExpression , TernaryExpression > =
174
+ DependentAttr
175
+ | ConditionalDependentAttr < BooleanExpression >
176
+ | ConditionalDependentAttrGroup < StringExpression >
177
+ | StaticClass
178
+ | DynamicClasses < TernaryExpression > ;
179
+
172
180
/**
173
181
* This class is used to track the styles and dynamic expressions on an element
174
182
* and produce a runtime class expression in conjunction with a style mapping.
@@ -208,11 +216,8 @@ export class ElementAnalysis<BooleanExpression, StringExpression, TernaryExpress
208
216
/** attributes set dynamically or depending on a dynamic class */
209
217
dynamicAttributes : Array < DynamicAttrs < BooleanExpression , StringExpression > > ;
210
218
211
- private addedStyles : Array < DependentAttr
212
- | ConditionalDependentAttr < BooleanExpression >
213
- | ConditionalDependentAttrGroup < StringExpression >
214
- | StaticClass
215
- | DynamicClasses < TernaryExpression > > ;
219
+ private explicitlyAddedStyles : Array < AnalyzedStyle < BooleanExpression , StringExpression , TernaryExpression > > ;
220
+ private addedStyles : Array < AnalyzedStyle < BooleanExpression , StringExpression , TernaryExpression > > ;
216
221
217
222
/** classes declared explicitly and found in at least one dynamic class expression. */
218
223
private dynamicClassExpressions : Map < BlockClass , DynamicClasses < TernaryExpression > > ;
@@ -250,6 +255,7 @@ export class ElementAnalysis<BooleanExpression, StringExpression, TernaryExpress
250
255
this . allStaticStyles = new Set ( ) ;
251
256
this . allAttributes = new Set ( ) ;
252
257
this . addedStyles = new Array ( ) ;
258
+ this . explicitlyAddedStyles = new Array ( ) ;
253
259
this . _sealed = false ;
254
260
this . _reservedClassNames = reservedClassNames ;
255
261
}
@@ -268,6 +274,17 @@ export class ElementAnalysis<BooleanExpression, StringExpression, TernaryExpress
268
274
return this . allClasses . get ( block ) ;
269
275
}
270
276
277
+ blocksFound ( ) : Array < Block > {
278
+ return new Array ( ...this . allClasses . keys ( ) ) ;
279
+ }
280
+
281
+ stylesFound ( ) : Array < Style > {
282
+ let styles = new Array < Style > ( ) ;
283
+ styles . push ( ...this . classesFound ( ) ) ;
284
+ styles . push ( ...this . attributesFound ( ) ) ;
285
+ return styles ;
286
+ }
287
+
271
288
/**
272
289
* Checks if the given class or block is set on this element,
273
290
* or if it is implied by one of the other styles on this element.
@@ -419,12 +436,18 @@ export class ElementAnalysis<BooleanExpression, StringExpression, TernaryExpress
419
436
return false ;
420
437
}
421
438
439
+ getSourceAnalysis ( ) : ElementSourceAnalysis < BooleanExpression , StringExpression , TernaryExpression > {
440
+ this . assertSealed ( true ) ;
441
+ return new ElementSourceAnalysis ( this . explicitlyAddedStyles ) ;
442
+ }
443
+
422
444
/**
423
445
* This method indicates that all styles have been added
424
446
* and can be analyzed and validated.
425
447
*/
426
448
seal ( ) {
427
449
this . assertSealed ( false ) ;
450
+ this . explicitlyAddedStyles = [ ...this . addedStyles ] ;
428
451
429
452
// After template analysis is done, we need to add all composed styles.
430
453
// Conflict validation is done at Block construction time for these.
@@ -509,20 +532,15 @@ export class ElementAnalysis<BooleanExpression, StringExpression, TernaryExpress
509
532
}
510
533
}
511
534
512
- let styles : [
513
- Array < StaticClass | DynamicClasses < TernaryExpression > > ,
514
- Array < DependentAttr | ConditionalDependentAttr < BooleanExpression > | ConditionalDependentAttrGroup < StringExpression > >
515
- ] = [ [ ] , [ ] ] ;
516
- let [ classStyles , attrStyles ] = this . addedStyles . reduce (
517
- ( res , style ) => {
518
- if ( isStaticClass ( style ) || isTrueCondition ( style ) || isFalseCondition ( style ) ) {
519
- res [ 0 ] . push ( style ) ;
520
- } else {
521
- res [ 1 ] . push ( style ) ;
522
- }
523
- return res ;
524
- } ,
525
- styles ) ;
535
+ let classStyles = new Array < StaticClass | DynamicClasses < TernaryExpression > > ( ) ;
536
+ let attrStyles = new Array < DependentAttr | ConditionalDependentAttr < BooleanExpression > | ConditionalDependentAttrGroup < StringExpression > > ( ) ;
537
+ for ( let style of this . addedStyles ) {
538
+ if ( isStaticClass ( style ) || isTrueCondition ( style ) || isFalseCondition ( style ) ) {
539
+ classStyles . push ( style ) ;
540
+ } else {
541
+ attrStyles . push ( style ) ;
542
+ }
543
+ }
526
544
527
545
for ( let classStyle of classStyles ) {
528
546
if ( isStaticClass ( classStyle ) ) {
@@ -935,7 +953,89 @@ export class ElementAnalysis<BooleanExpression, StringExpression, TernaryExpress
935
953
delete classes [ condition ] ;
936
954
}
937
955
}
956
+ }
938
957
958
+ /**
959
+ * The source analysis is useful when the code wants an exact view of what styles
960
+ * were explicitly set on an element.
961
+ */
962
+ export class ElementSourceAnalysis < BooleanExpression , StringExpression , TernaryExpression > {
963
+ addedStyles : AnalyzedStyle < BooleanExpression , StringExpression , TernaryExpression > [ ] ;
964
+ blocksFound : Set < Block > ;
965
+ stylesFound : Set < Style > ;
966
+ /**
967
+ * Styles that are always applied by the author.
968
+ * This includes styles that are disabled if a dynamic dependency isn't present. */
969
+ staticStyles : Array < Style > ;
970
+ ternaryStyles : Array < DynamicClasses < TernaryExpression > > ;
971
+ booleanStyles : Array < ConditionalAttr < BooleanExpression > > ;
972
+ switchStyles : Array < DynamicAttrGroup < StringExpression > > ;
973
+
974
+ constructor ( addedStyles : Array < AnalyzedStyle < BooleanExpression , StringExpression , TernaryExpression > > ) {
975
+ this . addedStyles = addedStyles ;
976
+ this . blocksFound = new Set ( ) ;
977
+ this . stylesFound = new Set < Style > ( ) ;
978
+ this . staticStyles = [ ] ;
979
+ this . ternaryStyles = [ ] ;
980
+ this . booleanStyles = [ ] ;
981
+ this . switchStyles = [ ] ;
982
+ for ( let s of addedStyles ) {
983
+ if ( isStaticClass ( s ) ) {
984
+ this . staticStyles . push ( s . klass ) ;
985
+ this . stylesFound . add ( s . klass ) ;
986
+ this . blocksFound . add ( s . klass . block ) ;
987
+ } else if ( isSwitch ( s ) ) {
988
+ this . switchStyles . push ( s ) ;
989
+ for ( let k of Object . keys ( s . group ) ) {
990
+ this . stylesFound . add ( s . group [ k ] ) ;
991
+ this . blocksFound . add ( s . group [ k ] . block ) ;
992
+ }
993
+ } else if ( isTrueCondition ( s ) || isFalseCondition ( s ) ) {
994
+ this . ternaryStyles . push ( s ) ;
995
+ if ( isTrueCondition ( s ) ) {
996
+ for ( let c of s . whenTrue ) {
997
+ this . stylesFound . add ( c ) ;
998
+ this . blocksFound . add ( c . block ) ;
999
+ }
1000
+ }
1001
+ if ( isFalseCondition ( s ) ) {
1002
+ for ( let c of s . whenFalse ) {
1003
+ this . stylesFound . add ( c ) ;
1004
+ this . blocksFound . add ( c . block ) ;
1005
+ }
1006
+ }
1007
+ } else if ( isConditional ( s ) ) {
1008
+ this . booleanStyles . push ( s ) ;
1009
+ for ( let style of s . value ) {
1010
+ this . stylesFound . add ( style ) ;
1011
+ this . blocksFound . add ( style . block ) ;
1012
+ }
1013
+ } else if ( hasDependency ( s ) ) {
1014
+ // we don't need to track dependencies in the source view because it's a
1015
+ // dynamic behavior that is decided by the style relationships and not
1016
+ // based on what was authored for the dependent style. Since it wasn't
1017
+ // also one of the dynamic types it must be static.
1018
+ for ( let style of s . value ) {
1019
+ this . staticStyles . push ( style ) ;
1020
+ this . stylesFound . add ( style ) ;
1021
+ this . blocksFound . add ( style . block ) ;
1022
+ }
1023
+ } else {
1024
+ assertNever ( s ) ;
1025
+ }
1026
+ }
1027
+ for ( let b1 of this . blocksFound ) {
1028
+ for ( let b2 of this . blocksFound ) {
1029
+ if ( b1 . isAncestorOf ( b2 ) ) {
1030
+ this . blocksFound . delete ( b1 ) ;
1031
+ }
1032
+ }
1033
+ }
1034
+ }
1035
+
1036
+ size ( ) : number {
1037
+ return this . addedStyles . length ;
1038
+ }
939
1039
}
940
1040
941
1041
function dynamicClassAndDependentAttrs < BooleanExpression , StringExpression > (
0 commit comments