Skip to content

Commit ff8795a

Browse files
committed
feat: Analyzer API updates and package re-naming.
- Promote Analyzer object from interface to class - MetaAnalysis => Analyzer - Added basic broccoli plugin - /Block => /BlockTree - /TemplateAnalysis => /Analyzer
1 parent 0399afa commit ff8795a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+856
-673
lines changed

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

+43-46
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,19 @@ import {
1919
} from "@opticss/util";
2020
import { IdentGenerator } from "opticss";
2121

22-
import { Block, Style } from "../Block";
2322
import { BlockFactory } from "../BlockParser";
23+
import { Block, Style } from "../BlockTree";
2424
import { ResolvedConfiguration } from "../configuration";
2525

2626
import { ElementAnalysis, SerializedElementAnalysis } from "./ElementAnalysis";
27-
import { StyleAnalysis } from "./StyleAnalysis";
2827
import { TemplateValidator, TemplateValidatorOptions } from "./validations";
2928

3029
/**
3130
* This interface defines a JSON friendly serialization
3231
* of a {TemplateAnalysis}.
3332
*/
34-
export interface SerializedTemplateAnalysis<K extends keyof TemplateTypes> {
35-
template: SerializedTemplateInfo<K>;
33+
export interface SerializedAnalysis {
34+
template: SerializedTemplateInfo<keyof TemplateTypes>;
3635
blocks: ObjectDictionary<string>;
3736
stylesFound: string[];
3837
// The numbers stored in each element are an index into a stylesFound;
@@ -48,9 +47,9 @@ export interface SerializedTemplateAnalysis<K extends keyof TemplateTypes> {
4847
* 2. Call [[addExclusiveStyle addExclusiveStyle(alwaysPresent, ...style)]] for all the styles used that are mutually exclusive on the current html element.
4948
* 3. Call [[endElement endElement()]] when done adding styles for the current element.
5049
*/
51-
export class TemplateAnalysis<K extends keyof TemplateTypes> implements StyleAnalysis {
50+
export class Analysis {
5251

53-
template: TemplateInfo<K>;
52+
template: TemplateInfo<keyof TemplateTypes>;
5453
idGenerator: IdentGenerator;
5554

5655
/**
@@ -148,7 +147,7 @@ export class TemplateAnalysis<K extends keyof TemplateTypes> implements StyleAna
148147
/**
149148
* @param template The template being analyzed.
150149
*/
151-
constructor(template: TemplateInfo<K>, options?: TemplateValidatorOptions) {
150+
constructor(template: TemplateInfo<keyof TemplateTypes>, options?: TemplateValidatorOptions) {
152151
this.idGenerator = new IdentGenerator();
153152
this.template = template;
154153
this.blocks = {};
@@ -276,7 +275,7 @@ export class TemplateAnalysis<K extends keyof TemplateTypes> implements StyleAna
276275
/**
277276
* Generates a [[SerializedTemplateAnalysis]] for this analysis.
278277
*/
279-
serialize(): SerializedTemplateAnalysis<K> {
278+
serialize(): SerializedAnalysis {
280279
let blocks = {};
281280
let stylesFound: string[] = [];
282281
let elements: ObjectDictionary<SerializedElementAnalysis> = {};
@@ -310,9 +309,8 @@ export class TemplateAnalysis<K extends keyof TemplateTypes> implements StyleAna
310309
elements[key] = el.serialize(styleIndexes);
311310
});
312311

313-
let t: SerializedTemplateInfo<K> = template;
314312
// Return serialized Analysis object.
315-
return { template: t, blocks, stylesFound, elements };
313+
return { template, blocks, stylesFound, elements };
316314
}
317315

318316
/**
@@ -321,13 +319,13 @@ export class TemplateAnalysis<K extends keyof TemplateTypes> implements StyleAna
321319
* @param options The plugin options that are used to parse the blocks.
322320
* @param postcssImpl The instance of postcss that should be used to parse the block's css.
323321
*/
324-
static deserialize<K extends keyof TemplateTypes>(
325-
serializedAnalysis: SerializedTemplateAnalysis<K>,
322+
static async deserialize (
323+
serializedAnalysis: SerializedAnalysis,
326324
blockFactory: BlockFactory,
327-
): Promise<TemplateAnalysis<K>> {
325+
): Promise<Analysis> {
328326
let blockNames = Object.keys(serializedAnalysis.blocks);
329-
let info = TemplateInfoFactory.deserialize<K>(serializedAnalysis.template);
330-
let analysis = new TemplateAnalysis(info);
327+
let info = TemplateInfoFactory.deserialize<keyof TemplateTypes>(serializedAnalysis.template);
328+
let analysis = new Analysis(info);
331329
let blockPromises = new Array<Promise<{name: string; block: Block}>>();
332330
blockNames.forEach(n => {
333331
let blockIdentifier = serializedAnalysis.blocks[n];
@@ -336,42 +334,41 @@ export class TemplateAnalysis<K extends keyof TemplateTypes> implements StyleAna
336334
});
337335
blockPromises.push(promise);
338336
});
339-
return Promise.all(blockPromises).then(values => {
340-
341-
// Create a temporary block so we can take advantage of `Block.lookup`
342-
// to easily resolve all BlockPaths referenced in the serialized analysis.
343-
// TODO: We may want to abstract this so we're not making a temporary block.
344-
let localScope = new Block("analysis-block", "tmp");
345-
values.forEach(o => {
346-
analysis.blocks[o.name] = o.block;
347-
localScope.addBlockReference(o.name, o.block);
348-
});
349-
let objects = new Array<Style>();
350-
serializedAnalysis.stylesFound.forEach(s => {
351-
let style = localScope.lookup(s);
352-
if (style) {
353-
objects.push(style);
354-
} else {
355-
throw new Error(`Cannot resolve ${s} to a block style.`);
356-
}
357-
});
358-
359-
let elementNames = Object.keys(serializedAnalysis.elements);
360-
elementNames.forEach((elID) => {
361-
let data = serializedAnalysis.elements[elID];
362-
let element = new ElementAnalysis<null, null, null>(data.sourceLocation || {start: POSITION_UNKNOWN}, undefined, elID);
363-
analysis.elements.set(elID, element);
364-
});
337+
let values = await Promise.all(blockPromises);
338+
339+
// Create a temporary block so we can take advantage of `Block.lookup`
340+
// to easily resolve all BlockPaths referenced in the serialized analysis.
341+
// TODO: We may want to abstract this so we're not making a temporary block.
342+
let localScope = new Block("analysis-block", "tmp");
343+
values.forEach(o => {
344+
analysis.blocks[o.name] = o.block;
345+
localScope.addBlockReference(o.name, o.block);
346+
});
347+
let objects = new Array<Style>();
348+
serializedAnalysis.stylesFound.forEach(s => {
349+
let style = localScope.lookup(s);
350+
if (style) {
351+
objects.push(style);
352+
} else {
353+
throw new Error(`Cannot resolve ${s} to a block style.`);
354+
}
355+
});
365356

366-
// tslint:disable-next-line:prefer-whatever-to-any
367-
return <TemplateAnalysis<K>> <any> analysis;
357+
let elementNames = Object.keys(serializedAnalysis.elements);
358+
elementNames.forEach((elID) => {
359+
let data = serializedAnalysis.elements[elID];
360+
let element = new ElementAnalysis<null, null, null>(data.sourceLocation || {start: POSITION_UNKNOWN}, undefined, elID);
361+
analysis.elements.set(elID, element);
368362
});
363+
364+
// tslint:disable-next-line:prefer-whatever-to-any
365+
return analysis;
369366
}
370367

371-
forOptimizer(config: ResolvedConfiguration): OptimizationTemplateAnalysis<K> {
372-
let optAnalysis = new OptimizationTemplateAnalysis<K>(this.template);
368+
forOptimizer(opts: ResolvedConfiguration): OptimizationTemplateAnalysis<keyof TemplateTypes> {
369+
let optAnalysis = new OptimizationTemplateAnalysis<keyof TemplateTypes>(this.template);
373370
for (let element of this.elements.values()) {
374-
let result = element.forOptimizer(config);
371+
let result = element.forOptimizer(opts);
375372
optAnalysis.elements.push(result[0]);
376373
}
377374
return optAnalysis;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
import {
2+
TemplateAnalysis as OptimizationAnalysis,
3+
TemplateInfo,
4+
TemplateIntegrationOptions,
5+
TemplateTypes,
6+
} from "@opticss/template-api";
7+
import { whatever } from "@opticss/util";
8+
import * as debugGenerator from "debug";
9+
10+
import { BlockFactory } from "../BlockParser";
11+
import { Block, Style } from "../BlockTree";
12+
import {
13+
Options,
14+
resolveConfiguration,
15+
ResolvedConfiguration,
16+
} from "../configuration";
17+
18+
import { Analysis, SerializedAnalysis } from "./Analysis";
19+
import { TemplateValidatorOptions } from "./validations";
20+
import { Configuration } from "webpack";
21+
22+
const debug = debugGenerator("css-blocks:analyzer");
23+
24+
const DEFAULT_OPTS = {
25+
rewriteIdents: {
26+
id: false,
27+
class: true,
28+
omitIdents: {
29+
id: [],
30+
class: [],
31+
},
32+
},
33+
analyzedAttributes: ["class"],
34+
analyzedTagnames: false,
35+
};
36+
37+
export interface AnalysisOptions {
38+
validations?: TemplateValidatorOptions;
39+
features?: TemplateIntegrationOptions;
40+
}
41+
42+
export interface SerializedAnalyzer {
43+
analyses: SerializedAnalysis[];
44+
}
45+
46+
export abstract class Analyzer<K extends keyof TemplateTypes> {
47+
public readonly blockFactory: BlockFactory;
48+
49+
public readonly validatorOptions: TemplateValidatorOptions;
50+
public readonly optimizationOptions: TemplateIntegrationOptions;
51+
public readonly cssBlocksOptions: ResolvedConfiguration;
52+
53+
protected analysisMap: Map<string, Analysis>;
54+
protected stylesFound: Map<Style, Analysis[]>;
55+
protected dynamicStyles: Map<Style, Analysis[]>;
56+
57+
constructor (
58+
options?: Options,
59+
analysisOpts?: AnalysisOptions,
60+
) {
61+
this.cssBlocksOptions = resolveConfiguration(options);
62+
this.validatorOptions = analysisOpts && analysisOpts.validations || {};
63+
this.optimizationOptions = analysisOpts && analysisOpts.features || DEFAULT_OPTS;
64+
this.blockFactory = new BlockFactory(this.cssBlocksOptions);
65+
this.analysisMap = new Map();
66+
this.stylesFound = new Map();
67+
this.dynamicStyles = new Map();
68+
}
69+
70+
abstract analyze(...entryPoints: string[]): Promise<Analyzer<keyof TemplateTypes>>;
71+
72+
// TODO: We don't really want to burn the world here.
73+
// We need more targeted Analysis / BlockFactory invalidation.
74+
public reset(): void {
75+
this.analysisMap = new Map();
76+
this.stylesFound = new Map();
77+
this.dynamicStyles = new Map();
78+
this.blockFactory.reset();
79+
}
80+
81+
newAnalysis(info: TemplateInfo<K>): Analysis {
82+
let analysis = new Analysis(info, this.validatorOptions);
83+
this.analysisMap.set(info.identifier, analysis);
84+
return analysis;
85+
}
86+
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+
}
96+
}
97+
98+
analysisCount(): number { return this.analysisMap.size; }
99+
100+
eachAnalysis(cb: (v: Analysis) => whatever) { this.analysisMap.forEach(cb); }
101+
102+
styleCount(): number { return this.stylesFound.size; }
103+
104+
dynamicCount(): number { return this.dynamicStyles.size; }
105+
106+
isDynamic(style: Style): boolean { return this.dynamicStyles.has(style); }
107+
108+
blockDependencies(): Set<Block> {
109+
let allBlocks = new Set<Block>();
110+
this.analysisMap.forEach(analysis => {
111+
allBlocks = new Set([...allBlocks, ...analysis.referencedBlocks()]);
112+
});
113+
return allBlocks;
114+
}
115+
116+
transitiveBlockDependencies(): Set<Block> {
117+
let allBlocks = new Set<Block>();
118+
this.analysisMap.forEach(analysis => {
119+
allBlocks = new Set<Block>([...allBlocks, ...analysis.transitiveBlockDependencies()]);
120+
});
121+
return allBlocks;
122+
}
123+
124+
analyses(): Analysis[] {
125+
let analyses: Analysis[] = [];
126+
this.eachAnalysis(a => analyses.push(a));
127+
return analyses;
128+
}
129+
130+
serialize(): SerializedAnalyzer {
131+
let analyses: SerializedAnalysis[] = [];
132+
this.eachAnalysis(a => {
133+
analyses.push(a.serialize());
134+
});
135+
return { analyses };
136+
}
137+
138+
forOptimizer(opts: ResolvedConfiguration): OptimizationAnalysis<keyof TemplateTypes>[] {
139+
let analyses = new Array<OptimizationAnalysis<keyof TemplateTypes>>();
140+
this.eachAnalysis(a => {
141+
analyses.push(a.forOptimizer(opts));
142+
});
143+
return analyses;
144+
}
145+
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+
}
155+
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import {
2828
isAttrValue,
2929
isBlockClass,
3030
Style,
31-
} from "../Block";
31+
} from "../BlockTree";
3232
import {
3333
ResolvedConfiguration,
3434
} from "../configuration";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export { Analysis, SerializedAnalysis } from "./Analysis";
2+
export { Analyzer, SerializedAnalyzer } from "./Analyzer";
3+
export { ElementAnalysis, SerializedElementAnalysis } from "./ElementAnalysis";
4+
export {
5+
TemplateValidator,
6+
TemplateValidators,
7+
TemplateValidatorOptions,
8+
} from "./validations";
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { whatever } from "@opticss/util";
22

3+
import { Analysis } from "../Analysis";
34
import { ElementAnalysis } from "../ElementAnalysis";
4-
import { StyleAnalysis } from "../StyleAnalysis";
55

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

packages/css-blocks/src/TemplateAnalysis/validations/attribute-group-validator.ts packages/css-blocks/src/Analyzer/validations/attribute-group-validator.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { unionInto } from "@opticss/util";
22

3-
import { Attribute, isAttrValue } from "../../Block";
3+
import { Attribute, isAttrValue } from "../../BlockTree";
44
import { isAttrGroup, isBooleanAttr } from "../ElementAnalysis";
55

66
import { ErrorCallback, Validator } from "./Validator";

packages/css-blocks/src/TemplateAnalysis/validations/class-pairs-validator.ts packages/css-blocks/src/Analyzer/validations/class-pairs-validator.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Block, BlockClass, isBlockClass } from "../../Block";
1+
import { Block, BlockClass, isBlockClass } from "../../BlockTree";
22
import { isFalseCondition, isTrueCondition } from "../ElementAnalysis";
33

44
import { ErrorCallback, Validator } from "./Validator";

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { ObjectDictionary } from "@opticss/util";
22

33
import * as errors from "../../errors";
4+
import { Analysis } from "../Analysis";
45
import { ElementAnalysis } from "../ElementAnalysis";
5-
import { StyleAnalysis } from "../StyleAnalysis";
66

77
import { Validator } from "./Validator";
88
import { attributeGroupValidator } from "./attribute-group-validator";
@@ -94,7 +94,7 @@ export class TemplateValidator {
9494
* @param locInfo Location info for the elements being validated.
9595
*/
9696
// tslint:disable-next-line:prefer-whatever-to-any
97-
validate(templateAnalysis: StyleAnalysis, element: ElementAnalysis<any, any, any>) {
97+
validate(templateAnalysis: Analysis, element: ElementAnalysis<any, any, any>) {
9898

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

packages/css-blocks/src/TemplateAnalysis/validations/property-conflict-validator.ts packages/css-blocks/src/Analyzer/validations/property-conflict-validator.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { MultiMap, objectValues, TwoKeyMultiMap } from "@opticss/util";
22
import * as propParser from "css-property-parser";
33
import * as postcss from "postcss";
44

5-
import { Ruleset, Style } from "../../Block";
5+
import { Ruleset, Style } from "../../BlockTree";
66
import {
77
isAttrGroup,
88
isBooleanAttr,

packages/css-blocks/src/BlockCompiler/ConflictResolver.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ import { CompoundSelector, ParsedSelector, parseSelector } from "opticss";
33
import * as postcss from "postcss";
44
import selectorParser = require("postcss-selector-parser");
55

6-
import { Block, Style } from "../Block";
76
import { getBlockNode } from "../BlockParser";
87
import { RESOLVE_RE } from "../BlockSyntax";
8+
import { Block, Style } from "../BlockTree";
99
import { SourceLocation, sourceLocation } from "../SourceLocation";
1010
import { ResolvedConfiguration } from "../configuration";
1111
import * as errors from "../errors";

0 commit comments

Comments
 (0)