Skip to content

Commit deefcdd

Browse files
committed
fix: Create a 'Source Analysis' for the new rewrite strategy.
The ElementAnalysis gets sealed and a bunch of styles get added into the mix based on composition and inheritance. This is ideal for conflict detection and for sending the analysis to opticss, but it's not ideal for our new rewrite strategy which will apply composition and inheritance after resolving runtime blocks and only takes as input the exact styles that have been applied by the source author. In order to prevent existing code from breaking, I've created a new analysis object called 'ElementSourceAnalysis' which is in charge of creating an "unresolved" view of the analysis. This is used in the new rewriter now.
1 parent 8ee7d51 commit deefcdd

File tree

6 files changed

+836
-138
lines changed

6 files changed

+836
-138
lines changed

packages/@css-blocks/core/src/Analyzer/ElementAnalysis.ts

+119-19
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
MultiMap,
1818
ObjectDictionary,
1919
objectValues,
20+
assertNever,
2021
} from "@opticss/util";
2122

2223
import {
@@ -169,6 +170,13 @@ export interface SerializedElementAnalysis {
169170
dynamicAttributes: Array<SerializedDynamicAttrs>;
170171
}
171172

173+
type AnalyzedStyle<BooleanExpression, StringExpression, TernaryExpression> =
174+
DependentAttr
175+
| ConditionalDependentAttr<BooleanExpression>
176+
| ConditionalDependentAttrGroup<StringExpression>
177+
| StaticClass
178+
| DynamicClasses<TernaryExpression>;
179+
172180
/**
173181
* This class is used to track the styles and dynamic expressions on an element
174182
* and produce a runtime class expression in conjunction with a style mapping.
@@ -208,11 +216,8 @@ export class ElementAnalysis<BooleanExpression, StringExpression, TernaryExpress
208216
/** attributes set dynamically or depending on a dynamic class */
209217
dynamicAttributes: Array<DynamicAttrs<BooleanExpression, StringExpression>>;
210218

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>>;
216221

217222
/** classes declared explicitly and found in at least one dynamic class expression. */
218223
private dynamicClassExpressions: Map<BlockClass, DynamicClasses<TernaryExpression>>;
@@ -250,6 +255,7 @@ export class ElementAnalysis<BooleanExpression, StringExpression, TernaryExpress
250255
this.allStaticStyles = new Set();
251256
this.allAttributes = new Set();
252257
this.addedStyles = new Array();
258+
this.explicitlyAddedStyles = new Array();
253259
this._sealed = false;
254260
this._reservedClassNames = reservedClassNames;
255261
}
@@ -268,6 +274,17 @@ export class ElementAnalysis<BooleanExpression, StringExpression, TernaryExpress
268274
return this.allClasses.get(block);
269275
}
270276

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+
271288
/**
272289
* Checks if the given class or block is set on this element,
273290
* or if it is implied by one of the other styles on this element.
@@ -419,12 +436,18 @@ export class ElementAnalysis<BooleanExpression, StringExpression, TernaryExpress
419436
return false;
420437
}
421438

439+
getSourceAnalysis(): ElementSourceAnalysis<BooleanExpression, StringExpression, TernaryExpression> {
440+
this.assertSealed(true);
441+
return new ElementSourceAnalysis(this.explicitlyAddedStyles);
442+
}
443+
422444
/**
423445
* This method indicates that all styles have been added
424446
* and can be analyzed and validated.
425447
*/
426448
seal() {
427449
this.assertSealed(false);
450+
this.explicitlyAddedStyles = [...this.addedStyles];
428451

429452
// After template analysis is done, we need to add all composed styles.
430453
// Conflict validation is done at Block construction time for these.
@@ -509,20 +532,15 @@ export class ElementAnalysis<BooleanExpression, StringExpression, TernaryExpress
509532
}
510533
}
511534

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+
}
526544

527545
for (let classStyle of classStyles) {
528546
if (isStaticClass(classStyle)) {
@@ -935,7 +953,89 @@ export class ElementAnalysis<BooleanExpression, StringExpression, TernaryExpress
935953
delete classes[condition];
936954
}
937955
}
956+
}
938957

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+
}
9391039
}
9401040

9411041
function dynamicClassAndDependentAttrs<BooleanExpression, StringExpression>(

packages/@css-blocks/ember/package.json

+3
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,15 @@
3131
"devDependencies": {
3232
"@css-blocks/code-style": "^1.0.0",
3333
"@css-blocks/glimmer": "^1.0.0",
34+
"@types/chai-as-promised": "^7.1.2",
3435
"@types/console-ui": "^2.2.3",
3536
"@types/core-object": "^3.0.1",
3637
"@types/express": "^4.17.6",
3738
"@types/fs-extra": "^8.0.0",
3839
"@types/glob": "^7.1.1",
3940
"broccoli-node-api": "^1.7.0",
4041
"broccoli-test-helper": "^2.0.0",
42+
"chai-as-promised": "^7.1.1",
4143
"ember-cli-htmlbars": "^4.3.1",
4244
"typescript": "~3.8.3",
4345
"watch": "^1.0.2"
@@ -48,6 +50,7 @@
4850
"dependencies": {
4951
"@css-blocks/config": "^1.0.0",
5052
"@css-blocks/core": "^1.0.0",
53+
"@css-blocks/glimmer": "^1.0.0",
5154
"@glimmer/compiler": "^0.43.0",
5255
"@glimmer/syntax": "^0.43.0",
5356
"@opticss/template-api": "^0.6.3",

0 commit comments

Comments
 (0)