Skip to content

Commit b8151ce

Browse files
amiller-ghchriseppstein
authored andcommitted
feat: The JSX and Webpack plugins use updated Analyzer APIs.
- JSX analyzer extends from css-blocks Analyzer and uses new APIs for getting Analyses. - Analysis objects propagate Style trackings up to parent Analyzer if present. - Analysis and TemplateInfo objects now take the template type generic string.
1 parent a60c1a1 commit b8151ce

38 files changed

+613
-803
lines changed

packages/css-blocks/src/Analyzer/Analysis.ts

+59-41
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import {
88
import {
99
SerializedTemplateInfo,
1010
TemplateAnalysis as OptimizationTemplateAnalysis,
11-
TemplateInfo,
1211
TemplateInfoFactory,
1312
TemplateIntegrationOptions,
1413
TemplateTypes,
@@ -23,6 +22,7 @@ import { BlockFactory } from "../BlockParser";
2322
import { Block, Style } from "../BlockTree";
2423
import { ResolvedConfiguration } from "../configuration";
2524

25+
import { Analyzer } from "./Analyzer";
2626
import { ElementAnalysis, SerializedElementAnalysis } from "./ElementAnalysis";
2727
import { TemplateValidator, TemplateValidatorOptions } from "./validations";
2828

@@ -47,10 +47,11 @@ export interface SerializedAnalysis {
4747
* 2. Call [[addExclusiveStyle addExclusiveStyle(alwaysPresent, ...style)]] for all the styles used that are mutually exclusive on the current html element.
4848
* 3. Call [[endElement endElement()]] when done adding styles for the current element.
4949
*/
50-
export class Analysis {
50+
export class Analysis<K extends keyof TemplateTypes> {
5151

52-
template: TemplateInfo<keyof TemplateTypes>;
5352
idGenerator: IdentGenerator;
53+
parent?: Analyzer<K>;
54+
template: TemplateTypes[K];
5455

5556
/**
5657
* A map from a local name for the block to the [[Block]].
@@ -59,13 +60,53 @@ export class Analysis {
5960
*/
6061
blocks: ObjectDictionary<Block>;
6162

63+
6264
/**
63-
* Return the number of blocks discovered in this Template.
65+
* A per-element correlation of styles used. The current correlation is added
66+
* to this list when [[endElement]] is called.
67+
*/
68+
// tslint:disable-next-line:prefer-whatever-to-any
69+
elements: Map<string, ElementAnalysis<any, any, any>>;
70+
71+
/**
72+
* The current element, created when calling [[startElement]].
73+
* The current element is unset after calling [[endElement]].
6474
*/
65-
blockCount(): number {
66-
return Object.keys(this.blocks).length;
75+
// tslint:disable-next-line:prefer-whatever-to-any
76+
currentElement: ElementAnalysis<any, any, any> | undefined;
77+
78+
/**
79+
* Template validator instance to verify blocks applied to an element.
80+
*/
81+
validator: TemplateValidator;
82+
83+
/**
84+
* @param template The template being analyzed.
85+
*/
86+
constructor(template: TemplateTypes[K], options?: TemplateValidatorOptions, parent?: Analyzer<K>,) {
87+
this.idGenerator = new IdentGenerator();
88+
this.parent = parent;
89+
this.template = template;
90+
this.blocks = {};
91+
this.elements = new Map();
92+
this.validator = new TemplateValidator(options);
6793
}
6894

95+
/**
96+
* Return the number of blocks discovered in this Template.
97+
*/
98+
blockCount(): number { return Object.keys(this.blocks).length; }
99+
100+
/**
101+
* Convenience setter for adding a block to the template scope.
102+
*/
103+
addBlock(name: string, block: Block) { this.blocks[name] = block; }
104+
105+
/**
106+
* Convenience getter for fetching a block from the template scope.
107+
*/
108+
getBlock(name: string): Block { return this.blocks[name]; }
109+
69110
/**
70111
* Return the number of styles discovered in this Analysis' Template.
71112
* This is slow.
@@ -83,26 +124,10 @@ export class Analysis {
83124
return c;
84125
}
85126

86-
/**
87-
* A per-element correlation of styles used. The current correlation is added
88-
* to this list when [[endElement]] is called.
89-
*/
90-
// tslint:disable-next-line:prefer-whatever-to-any
91-
elements: Map<string, ElementAnalysis<any, any, any>>;
92-
93-
/**
94-
* The current element, created when calling [[startElement]].
95-
* The current element is unset after calling [[endElement]].
96-
*/
97-
// tslint:disable-next-line:prefer-whatever-to-any
98-
currentElement: ElementAnalysis<any, any, any> | undefined;
99-
100127
/**
101128
* Return the number of elements discovered in this Analysis.
102129
*/
103-
elementCount(): number {
104-
return this.elements.size;
105-
}
130+
elementCount(): number { return this.elements.size; }
106131

107132
/**
108133
* Get the nth element discovered in this Analysis.
@@ -139,22 +164,6 @@ export class Analysis {
139164
return this.elements.get(id);
140165
}
141166

142-
/**
143-
* Template validator instance to verify blocks applied to an element.
144-
*/
145-
validator: TemplateValidator;
146-
147-
/**
148-
* @param template The template being analyzed.
149-
*/
150-
constructor(template: TemplateInfo<keyof TemplateTypes>, options?: TemplateValidatorOptions) {
151-
this.idGenerator = new IdentGenerator();
152-
this.template = template;
153-
this.blocks = {};
154-
this.elements = new Map();
155-
this.validator = new TemplateValidator(options);
156-
}
157-
158167
/**
159168
* @param block The block for which the local name should be returned.
160169
* @return The local name of the given block.
@@ -218,6 +227,14 @@ export class Analysis {
218227
}
219228
this.validator.validate(this, element);
220229
this.elements.set(element.id, element);
230+
if (this.parent) {
231+
for (let s of [...element.classesFound(false), ...element.attributesFound(false)]) {
232+
this.parent.saveStaticStyle(s, this);
233+
}
234+
for (let s of [...element.classesFound(true), ...element.attributesFound(true)]) {
235+
this.parent.saveDynamicStyle(s, this);
236+
}
237+
}
221238
}
222239

223240
/**
@@ -322,10 +339,11 @@ export class Analysis {
322339
static async deserialize (
323340
serializedAnalysis: SerializedAnalysis,
324341
blockFactory: BlockFactory,
325-
): Promise<Analysis> {
342+
parent: Analyzer<keyof TemplateTypes>
343+
): Promise<Analysis<keyof TemplateTypes>> {
326344
let blockNames = Object.keys(serializedAnalysis.blocks);
327345
let info = TemplateInfoFactory.deserialize<keyof TemplateTypes>(serializedAnalysis.template);
328-
let analysis = new Analysis(info);
346+
let analysis = new Analysis(info, {}, parent);
329347
let blockPromises = new Array<Promise<{name: string; block: Block}>>();
330348
blockNames.forEach(n => {
331349
let blockIdentifier = serializedAnalysis.blocks[n];

packages/css-blocks/src/Analyzer/Analyzer.ts

+27-37
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
TemplateIntegrationOptions,
55
TemplateTypes,
66
} from "@opticss/template-api";
7-
import { whatever } from "@opticss/util";
7+
import { MultiMap, whatever } from "@opticss/util";
88
import * as debugGenerator from "debug";
99

1010
import { BlockFactory } from "../BlockParser";
@@ -17,7 +17,6 @@ import {
1717

1818
import { Analysis, SerializedAnalysis } from "./Analysis";
1919
import { TemplateValidatorOptions } from "./validations";
20-
import { Configuration } from "webpack";
2120

2221
const debug = debugGenerator("css-blocks:analyzer");
2322

@@ -50,9 +49,9 @@ export abstract class Analyzer<K extends keyof TemplateTypes> {
5049
public readonly optimizationOptions: TemplateIntegrationOptions;
5150
public readonly cssBlocksOptions: ResolvedConfiguration;
5251

53-
protected analysisMap: Map<string, Analysis>;
54-
protected stylesFound: Map<Style, Analysis[]>;
55-
protected dynamicStyles: Map<Style, Analysis[]>;
52+
protected analysisMap: Map<string, Analysis<K>>;
53+
protected staticStyles: MultiMap<Style, Analysis<K>>;
54+
protected dynamicStyles: MultiMap<Style, Analysis<K>>;
5655

5756
constructor (
5857
options?: Options,
@@ -63,43 +62,49 @@ export abstract class Analyzer<K extends keyof TemplateTypes> {
6362
this.optimizationOptions = analysisOpts && analysisOpts.features || DEFAULT_OPTS;
6463
this.blockFactory = new BlockFactory(this.cssBlocksOptions);
6564
this.analysisMap = new Map();
66-
this.stylesFound = new Map();
67-
this.dynamicStyles = new Map();
65+
this.staticStyles = new MultiMap();
66+
this.dynamicStyles = new MultiMap();
6867
}
6968

7069
abstract analyze(...entryPoints: string[]): Promise<Analyzer<keyof TemplateTypes>>;
7170

7271
// TODO: We don't really want to burn the world here.
7372
// We need more targeted Analysis / BlockFactory invalidation.
7473
public reset(): void {
74+
debug(`Resetting Analyzer.`);
7575
this.analysisMap = new Map();
76-
this.stylesFound = new Map();
77-
this.dynamicStyles = new Map();
76+
this.staticStyles = new MultiMap();
77+
this.dynamicStyles = new MultiMap();
7878
this.blockFactory.reset();
7979
}
8080

81-
newAnalysis(info: TemplateInfo<K>): Analysis {
82-
let analysis = new Analysis(info, this.validatorOptions);
81+
newAnalysis(info: TemplateInfo<K>): Analysis<K> {
82+
let analysis = new Analysis<K>(info, this.validatorOptions, this);
8383
this.analysisMap.set(info.identifier, analysis);
8484
return analysis;
8585
}
8686

87-
addAnalysis(analysis: Analysis) {
88-
debug(`MetaAnalysis: Adding analysis for ${analysis.template.identifier}`);
89-
this.analysisMap.set(analysis.template.identifier, analysis);
90-
for (let style of analysis.stylesFound()) {
91-
this.addAnalysisToStyleMap(this.stylesFound, style, analysis);
92-
}
93-
for (let style of analysis.stylesFound(true)) {
94-
this.addAnalysisToStyleMap(this.dynamicStyles, style, analysis);
95-
}
87+
saveStaticStyle(style: Style, analysis: Analysis<K>) {
88+
this.staticStyles.set(style, analysis);
9689
}
9790

91+
saveDynamicStyle(style: Style, analysis: Analysis<K>) {
92+
this.dynamicStyles.set(style, analysis);
93+
}
94+
95+
getAnalysis(idx: number): Analysis<K> { return this.analyses()[idx]; }
96+
9897
analysisCount(): number { return this.analysisMap.size; }
9998

100-
eachAnalysis(cb: (v: Analysis) => whatever) { this.analysisMap.forEach(cb); }
99+
eachAnalysis(cb: (v: Analysis<K>) => whatever) { this.analysisMap.forEach(cb); }
100+
101+
analyses(): Analysis<K>[] {
102+
let analyses: Analysis<K>[] = [];
103+
this.eachAnalysis(a => analyses.push(a));
104+
return analyses;
105+
}
101106

102-
styleCount(): number { return this.stylesFound.size; }
107+
styleCount(): number { return this.staticStyles.size; }
103108

104109
dynamicCount(): number { return this.dynamicStyles.size; }
105110

@@ -121,12 +126,6 @@ export abstract class Analyzer<K extends keyof TemplateTypes> {
121126
return allBlocks;
122127
}
123128

124-
analyses(): Analysis[] {
125-
let analyses: Analysis[] = [];
126-
this.eachAnalysis(a => analyses.push(a));
127-
return analyses;
128-
}
129-
130129
serialize(): SerializedAnalyzer {
131130
let analyses: SerializedAnalysis[] = [];
132131
this.eachAnalysis(a => {
@@ -143,13 +142,4 @@ export abstract class Analyzer<K extends keyof TemplateTypes> {
143142
return analyses;
144143
}
145144

146-
private addAnalysisToStyleMap(map: Map<Style, Analysis[]>, style: Style, analysis: Analysis) {
147-
let analyses = map.get(style);
148-
if (analyses) {
149-
analyses.push(analysis);
150-
} else {
151-
analyses = [analysis];
152-
}
153-
map.set(style, analyses);
154-
}
155145
}

packages/css-blocks/src/Analyzer/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export { Analysis, SerializedAnalysis } from "./Analysis";
22
export { Analyzer, SerializedAnalyzer } from "./Analyzer";
3-
export { ElementAnalysis, SerializedElementAnalysis } from "./ElementAnalysis";
3+
export * from "./ElementAnalysis";
44
export {
55
TemplateValidator,
66
TemplateValidators,
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
import { TemplateTypes } from "@opticss/template-api";
12
import { whatever } from "@opticss/util";
23

34
import { Analysis } from "../Analysis";
45
import { ElementAnalysis } from "../ElementAnalysis";
56

67
export type ErrorCallback = (str: string, loc?: null, details?: string) => void;
7-
export type Validator = (analysis: ElementAnalysis<whatever, whatever, whatever>, templateAnalysis: Analysis, err: ErrorCallback) => void;
8+
export type Validator = (analysis: ElementAnalysis<whatever, whatever, whatever>, templateAnalysis: Analysis<keyof TemplateTypes>, err: ErrorCallback) => void;

packages/css-blocks/src/Analyzer/validations/index.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { TemplateTypes } from "@opticss/template-api";
12
import { ObjectDictionary } from "@opticss/util";
23

34
import * as errors from "../../errors";
@@ -94,7 +95,7 @@ export class TemplateValidator {
9495
* @param locInfo Location info for the elements being validated.
9596
*/
9697
// tslint:disable-next-line:prefer-whatever-to-any
97-
validate(templateAnalysis: Analysis, element: ElementAnalysis<any, any, any>) {
98+
validate(templateAnalysis: Analysis<keyof TemplateTypes>, element: ElementAnalysis<any, any, any>) {
9899

99100
function err (message: string, locInfo?: errors.ErrorLocation | undefined | null, details?: string) {
100101
throw new errors.TemplateAnalysisError(

packages/css-blocks/src/BlockTree/RulesetContainer.ts

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ export type Resolution<S extends Styles = Styles> = {
4343
*/
4444
function expandProp(prop: string, value: string): propParser.Declarations {
4545
let expanded: propParser.Declarations = {};
46+
value = value.replace(/var\([^\)]+\)/gi, "inherit");
4647
if (propParser.isValidDeclaration(prop, value)) {
4748
expanded = propParser.expandShorthandProperty(prop, value, true, false);
4849
}

packages/css-blocks/src/TemplateRewriter/StyleMapping.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { StyleMapping as OptimizedMapping } from "@opticss/template-api";
1+
import { StyleMapping as OptimizedMapping, TemplateTypes } from "@opticss/template-api";
22

33
import { Analysis } from "../Analyzer";
44
import { ElementAnalysis } from "../Analyzer";
@@ -7,9 +7,9 @@ import { ResolvedConfiguration } from "../configuration";
77

88
import { IndexedClassRewrite } from "./ClassRewrite";
99
import { IndexedClassMapping, RewriteMapping } from "./RewriteMapping";
10-
export class StyleMapping {
10+
export class StyleMapping<T extends keyof TemplateTypes> {
1111
/** The analyses that were used to create this mapping. */
12-
analyses: Array<Analysis> | undefined;
12+
analyses: Array<Analysis<T>> | undefined;
1313
/** The blocks that were used to create this mapping. */
1414
blocks: Set<Block>;
1515
private configuration: ResolvedConfiguration;
@@ -19,7 +19,7 @@ export class StyleMapping {
1919
optimizedMap: OptimizedMapping,
2020
blocks: Iterable<Block>,
2121
configuration: ResolvedConfiguration,
22-
analyses?: Array<Analysis>,
22+
analyses?: Array<Analysis<T>>,
2323
) {
2424
this.configuration = configuration;
2525
this.optimizedMap = optimizedMap;

packages/css-blocks/test/opticss-test.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export class TemplateAnalysisTests {
3535
}
3636
}
3737
private useBlockStyles(
38-
analysis: Analysis, block: Block, blockName: string,
38+
analysis: Analysis<"Opticss.Template">, block: Block, blockName: string,
3939
useAttrsCallback?: (container: BlockClass, element: ElementAnalysis<whatever, whatever, whatever>) => void,
4040
) {
4141
analysis.blocks[blockName] = block;
@@ -64,7 +64,7 @@ export class TemplateAnalysisTests {
6464
`;
6565
class TestAnalyzer extends Analyzer<"Opticss.Template"> {
6666
analyze(): Promise<TestAnalyzer> {
67-
let analysis = new Analysis(info);
67+
let analysis = this.newAnalysis(info);
6868
let root = postcss.parse(css, { from: filename });
6969

7070
return this.blockFactory.parse(root, filename, "optimized").then((block: Block) => {
@@ -101,7 +101,7 @@ export class TemplateAnalysisTests {
101101
.f { font-size: 26px; }
102102
`);
103103
let analyses = analyzer.analyses();
104-
let blockMapping = new StyleMapping(optimized.styleMapping, [block], config, analyses);
104+
let blockMapping = new StyleMapping<"Opticss.Template">(optimized.styleMapping, [block], config, analyses);
105105
let it = analyses[0].elements.values();
106106
let element1 = it.next().value;
107107
let rewrite1 = blockMapping.rewriteMapping(element1);

0 commit comments

Comments
 (0)