Skip to content

Commit a60c1a1

Browse files
amiller-ghchriseppstein
authored andcommittedApr 12, 2018
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 32895fd commit a60c1a1

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

+851
-672
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+
}

0 commit comments

Comments
 (0)
Please sign in to comment.