1use std::collections::BTreeSet;
8
9use omena_parser::{
10 ParsedAnimationFactKind, ParsedCssModuleComposesEdgeKind, ParsedCssModuleComposesFactKind,
11 ParsedCssModuleValueFactKind, ParsedIcssFactKind, ParsedSelectorFactKind, StyleDialect,
12 collect_style_facts,
13};
14use serde::Serialize;
15
16use crate::Stylesheet;
17
18#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
19#[serde(rename_all = "camelCase")]
20pub struct CssModulesSemanticSummaryV0 {
21 pub schema_version: &'static str,
22 pub product: &'static str,
23 pub status: &'static str,
24 pub resolution_scope: &'static str,
25 pub class_export_count: usize,
26 pub class_export_names: Vec<String>,
27 pub composes_edge_seed_count: usize,
28 pub composes_local_edge_count: usize,
29 pub composes_global_edge_count: usize,
30 pub composes_external_edge_count: usize,
31 pub composes_target_names: Vec<String>,
32 pub composes_import_sources: Vec<String>,
33 pub value_edge_seed_count: usize,
34 pub value_import_edge_count: usize,
35 pub value_definition_edge_count: usize,
36 pub value_definition_names: Vec<String>,
37 pub value_reference_names: Vec<String>,
38 pub value_import_sources: Vec<String>,
39 pub icss_edge_seed_count: usize,
40 pub icss_import_edge_count: usize,
41 pub icss_export_edge_count: usize,
42 pub icss_export_names: Vec<String>,
43 pub icss_import_local_names: Vec<String>,
44 pub icss_import_remote_names: Vec<String>,
45 pub icss_import_sources: Vec<String>,
46 pub keyframe_names: Vec<String>,
47 pub animation_reference_names: Vec<String>,
48 pub capabilities: CssModulesSemanticCapabilitiesV0,
49 pub next_priorities: Vec<&'static str>,
50}
51
52#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
53#[serde(rename_all = "camelCase")]
54pub struct CssModulesSemanticCapabilitiesV0 {
55 pub parser_fact_surface_ready: bool,
56 pub per_file_symbol_summary_ready: bool,
57 pub composes_edge_seed_ready: bool,
58 pub value_edge_seed_ready: bool,
59 pub icss_edge_seed_ready: bool,
60 pub animation_edge_seed_ready: bool,
61 pub cross_file_resolution_ready: bool,
62 pub composes_closure_ready: bool,
63 pub value_graph_resolution_ready: bool,
64 pub cycle_detection_ready: bool,
65}
66
67pub fn summarize_css_modules_semantics(sheet: &Stylesheet) -> CssModulesSemanticSummaryV0 {
68 summarize_css_modules_semantics_for_source(sheet.source.as_str(), sheet.language)
69}
70
71pub fn summarize_css_modules_semantics_from_source(
72 style_path: &str,
73 style_source: &str,
74) -> Option<CssModulesSemanticSummaryV0> {
75 let dialect = dialect_for_style_path(style_path)?;
76 Some(summarize_css_modules_semantics_for_source(
77 style_source,
78 dialect,
79 ))
80}
81
82fn summarize_css_modules_semantics_for_source(
83 style_source: &str,
84 dialect: StyleDialect,
85) -> CssModulesSemanticSummaryV0 {
86 let facts = collect_style_facts(style_source, dialect);
87 let mut class_export_names = BTreeSet::new();
88 let mut composes_target_names = BTreeSet::new();
89 let mut composes_import_sources = BTreeSet::new();
90 let mut composes_local_edge_count = 0usize;
91 let mut composes_global_edge_count = 0usize;
92 let mut composes_external_edge_count = 0usize;
93 let mut value_definition_names = BTreeSet::new();
94 let mut value_reference_names = BTreeSet::new();
95 let mut value_import_sources = BTreeSet::new();
96 let mut icss_export_names = BTreeSet::new();
97 let mut icss_import_local_names = BTreeSet::new();
98 let mut icss_import_remote_names = BTreeSet::new();
99 let mut icss_import_sources = BTreeSet::new();
100 let mut keyframe_names = BTreeSet::new();
101 let mut animation_reference_names = BTreeSet::new();
102
103 for selector in facts.selectors {
104 if selector.kind == ParsedSelectorFactKind::Class {
105 class_export_names.insert(selector.name);
106 }
107 }
108
109 for composes in facts.css_module_composes {
110 match composes.kind {
111 ParsedCssModuleComposesFactKind::Target => {
112 composes_target_names.insert(composes.name);
113 }
114 ParsedCssModuleComposesFactKind::ImportSource => {
115 composes_import_sources.insert(composes.name);
116 }
117 }
118 }
119 for edge in facts.css_module_composes_edges {
120 match edge.kind {
121 ParsedCssModuleComposesEdgeKind::Local => composes_local_edge_count += 1,
122 ParsedCssModuleComposesEdgeKind::Global => composes_global_edge_count += 1,
123 ParsedCssModuleComposesEdgeKind::External => composes_external_edge_count += 1,
124 }
125 }
126
127 for value in facts.css_module_values {
128 match value.kind {
129 ParsedCssModuleValueFactKind::Definition => {
130 value_definition_names.insert(value.name);
131 }
132 ParsedCssModuleValueFactKind::Reference => {
133 value_reference_names.insert(value.name);
134 }
135 ParsedCssModuleValueFactKind::ImportSource => {
136 value_import_sources.insert(value.name);
137 }
138 }
139 }
140
141 for icss in facts.icss {
142 match icss.kind {
143 ParsedIcssFactKind::ExportName => {
144 icss_export_names.insert(icss.name);
145 }
146 ParsedIcssFactKind::ImportLocalName => {
147 icss_import_local_names.insert(icss.name);
148 }
149 ParsedIcssFactKind::ImportRemoteName => {
150 icss_import_remote_names.insert(icss.name);
151 }
152 ParsedIcssFactKind::ImportSource => {
153 icss_import_sources.insert(icss.name);
154 }
155 }
156 }
157
158 for animation in facts.animations {
159 match animation.kind {
160 ParsedAnimationFactKind::KeyframesDeclaration => {
161 keyframe_names.insert(animation.name);
162 }
163 ParsedAnimationFactKind::AnimationNameReference => {
164 animation_reference_names.insert(animation.name);
165 }
166 }
167 }
168
169 let class_export_names: Vec<_> = class_export_names.into_iter().collect();
170 let composes_target_names: Vec<_> = composes_target_names.into_iter().collect();
171 let composes_import_sources: Vec<_> = composes_import_sources.into_iter().collect();
172 let value_definition_names: Vec<_> = value_definition_names.into_iter().collect();
173 let value_reference_names: Vec<_> = value_reference_names.into_iter().collect();
174 let value_import_sources: Vec<_> = value_import_sources.into_iter().collect();
175 let icss_export_names: Vec<_> = icss_export_names.into_iter().collect();
176 let icss_import_local_names: Vec<_> = icss_import_local_names.into_iter().collect();
177 let icss_import_remote_names: Vec<_> = icss_import_remote_names.into_iter().collect();
178 let icss_import_sources: Vec<_> = icss_import_sources.into_iter().collect();
179 let keyframe_names: Vec<_> = keyframe_names.into_iter().collect();
180 let animation_reference_names: Vec<_> = animation_reference_names.into_iter().collect();
181
182 CssModulesSemanticSummaryV0 {
183 schema_version: "0",
184 product: "omena-semantic.css-modules-semantics",
185 status: "parserFactSeed",
186 resolution_scope: "perFileFactSummary",
187 class_export_count: class_export_names.len(),
188 class_export_names,
189 composes_edge_seed_count: composes_local_edge_count
190 + composes_global_edge_count
191 + composes_external_edge_count,
192 composes_local_edge_count,
193 composes_global_edge_count,
194 composes_external_edge_count,
195 composes_target_names,
196 composes_import_sources,
197 value_edge_seed_count: facts.css_module_value_import_edge_count
198 + facts.css_module_value_definition_edge_count,
199 value_import_edge_count: facts.css_module_value_import_edge_count,
200 value_definition_edge_count: facts.css_module_value_definition_edge_count,
201 value_definition_names,
202 value_reference_names,
203 value_import_sources,
204 icss_edge_seed_count: facts.icss_import_edge_count + facts.icss_export_edge_count,
205 icss_import_edge_count: facts.icss_import_edge_count,
206 icss_export_edge_count: facts.icss_export_edge_count,
207 icss_export_names,
208 icss_import_local_names,
209 icss_import_remote_names,
210 icss_import_sources,
211 keyframe_names,
212 animation_reference_names,
213 capabilities: CssModulesSemanticCapabilitiesV0 {
214 parser_fact_surface_ready: true,
215 per_file_symbol_summary_ready: true,
216 composes_edge_seed_ready: true,
217 value_edge_seed_ready: true,
218 icss_edge_seed_ready: true,
219 animation_edge_seed_ready: true,
220 cross_file_resolution_ready: false,
221 composes_closure_ready: false,
222 value_graph_resolution_ready: false,
223 cycle_detection_ready: false,
224 },
225 next_priorities: vec![
226 "crossFileComposesResolution",
227 "cssModulesValueGraphResolution",
228 "icssImportExportResolution",
229 "cycleDetection",
230 ],
231 }
232}
233
234fn dialect_for_style_path(style_path: &str) -> Option<StyleDialect> {
235 if style_path.ends_with(".module.css") || style_path.ends_with(".css") {
236 Some(StyleDialect::Css)
237 } else if style_path.ends_with(".module.scss") || style_path.ends_with(".scss") {
238 Some(StyleDialect::Scss)
239 } else if style_path.ends_with(".module.sass") || style_path.ends_with(".sass") {
240 Some(StyleDialect::Sass)
241 } else if style_path.ends_with(".module.less") || style_path.ends_with(".less") {
242 Some(StyleDialect::Less)
243 } else {
244 None
245 }
246}