forked from linkedin/css-blocks
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathAnalyzer.ts
150 lines (124 loc) · 4.8 KB
/
Analyzer.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
import {
TemplateAnalysis as OptimizationAnalysis,
TemplateIntegrationOptions,
TemplateTypes,
} from "@opticss/template-api";
import { MultiMap } from "@opticss/util";
import * as debugGenerator from "debug";
import { BlockFactory } from "../BlockParser";
import { Block, Style } from "../BlockTree";
import { ResolvedConfiguration } from "../configuration";
import { Analysis, SerializedAnalysis } from "./Analysis";
import { TemplateValidatorOptions } from "./validations";
const debug = debugGenerator("css-blocks:analyzer");
export interface AnalysisOptions {
validations?: TemplateValidatorOptions;
features?: TemplateIntegrationOptions;
}
export interface SerializedAnalyzer<K extends keyof TemplateTypes> {
analyses: SerializedAnalysis<K>[];
}
export abstract class Analyzer<K extends keyof TemplateTypes> {
public readonly blockFactory: BlockFactory;
public readonly validatorOptions: TemplateValidatorOptions;
public readonly cssBlocksOptions: ResolvedConfiguration;
protected analysisMap: Map<string, Analysis<K>>;
protected staticStyles: MultiMap<Style, Analysis<K>>;
protected dynamicStyles: MultiMap<Style, Analysis<K>>;
public stylesFound: Set<Style>;
constructor (
blockFactory: BlockFactory,
analysisOpts?: AnalysisOptions,
) {
// TODO: Remove April 2019 when Node.js 6 is EOL'd
if (parseInt(process.versions.node) <= 6) {
throw new Error("CSS Blocks does not support Node.js <= 6");
}
this.blockFactory = blockFactory;
this.cssBlocksOptions = blockFactory.configuration;
this.validatorOptions = analysisOpts && analysisOpts.validations || {};
this.analysisMap = new Map();
this.staticStyles = new MultiMap();
this.dynamicStyles = new MultiMap();
this.stylesFound = new Set();
}
abstract analyze(dir: string, entryPoints: string[]): Promise<Analyzer<K>>;
abstract get optimizationOptions(): TemplateIntegrationOptions;
// TODO: We don't really want to burn the world here.
// We need more targeted Analysis / BlockFactory invalidation.
public reset(): void {
debug(`Resetting Analyzer.`);
this.analysisMap = new Map();
this.staticStyles = new MultiMap();
this.dynamicStyles = new MultiMap();
this.stylesFound = new Set();
this.blockFactory.reset();
}
newAnalysis(info: TemplateTypes[K]): Analysis<K> {
let analysis = new Analysis<K>(info, this.validatorOptions, (element) => {
for (let s of element.stylesFound()) {
this.stylesFound.add(s);
}
for (let s of [...element.classesFound(false), ...element.attributesFound(false)]) {
this.staticStyles.set(s, analysis);
}
for (let s of [...element.classesFound(true), ...element.attributesFound(true)]) {
this.dynamicStyles.set(s, analysis);
}
});
this.analysisMap.set(info.identifier, analysis);
return analysis;
}
getAnalysis(idx: number): Analysis<K> { return this.analyses()[idx]; }
analysisCount(): number { return this.analysisMap.size; }
eachAnalysis(cb: (v: Analysis<K>) => unknown) { this.analysisMap.forEach(cb); }
analyses(): Analysis<K>[] {
let analyses: Analysis<K>[] = [];
this.eachAnalysis(a => analyses.push(a));
return analyses;
}
staticCount(): number { return this.staticStyles.size; }
dynamicCount(): number { return this.dynamicStyles.size; }
isDynamic(style: Style): boolean { return this.dynamicStyles.has(style); }
blockDependencies(): Set<Block> {
let allBlocks = new Set<Block>();
this.analysisMap.forEach(analysis => {
allBlocks = new Set([...allBlocks, ...analysis.referencedBlocks()]);
});
return allBlocks;
}
transitiveBlockDependencies(): Set<Block> {
let allBlocks = new Set<Block>();
this.analysisMap.forEach(analysis => {
allBlocks = new Set<Block>([...allBlocks, ...analysis.transitiveBlockDependencies()]);
});
return allBlocks;
}
/**
* Iterates through all the analyses objects for all the templates and
* creates a set of reservedClassNames here. These are used by the block
* compiler to ensure the classnames that are output don't collide with user
* specified style aliases.
*/
reservedClassNames(): Set<string> {
let allReservedClassNames = new Set<string>();
this.analysisMap.forEach(analysis => {
allReservedClassNames = new Set<string>([...allReservedClassNames, ...analysis.reservedClassNames()]);
});
return allReservedClassNames;
}
serialize(): SerializedAnalyzer<K> {
let analyses: SerializedAnalysis<K>[] = [];
this.eachAnalysis(a => {
analyses.push(a.serialize());
});
return { analyses };
}
forOptimizer(config: ResolvedConfiguration): OptimizationAnalysis<K>[] {
let analyses = new Array<OptimizationAnalysis<K>>();
this.eachAnalysis(a => {
analyses.push(a.forOptimizer(config));
});
return analyses;
}
}