1use engine_input_producers::EngineInputV2;
2use omena_semantic::{
3 CssModulesSemanticSummaryV0, DesignTokenSemanticSummaryV0, LosslessCstContractV0,
4 ParserBoundarySyntaxFactsV0, SelectorIdentityEngineSummaryV0, StyleSemanticBoundarySummaryV0,
5 StyleSemanticFactsV0, Stylesheet,
6};
7pub use omena_semantic::{
8 DesignTokenExternalDeclarationCandidateScopeV0, DesignTokenWorkspaceDeclarationFactV0,
9 ParserRangeV0 as OmenaBridgeParserRangeV0,
10};
11use serde::Serialize;
12
13mod bundler_config_alias;
14mod promotion_evidence;
15mod selector_references;
16mod source_evidence;
17mod source_imports;
18mod source_language;
19mod source_syntax;
20mod style_resolution;
21
22pub use bundler_config_alias::{
23 OmenaBridgeBundlerAliasUnrecognizedEntryV0, OmenaBridgeBundlerPathAliasMappingV0,
24 OmenaBridgeBundlerPathAliasSummaryV0, summarize_omena_bridge_bundler_path_aliases_for_config,
25};
26pub use promotion_evidence::{
27 SemanticPromotionEvidenceItemV0, SemanticPromotionEvidenceSummaryV0,
28 summarize_omena_bridge_promotion_evidence_with_source_input,
29 summarize_omena_bridge_semantic_promotion_evidence,
30};
31pub use selector_references::{
32 SelectorEditableDirectReferenceSiteV0, SelectorReferenceEngineSummaryV0,
33 SelectorReferenceSiteV0, SelectorReferenceSummaryV0,
34 summarize_omena_bridge_selector_reference_engine,
35};
36pub use source_evidence::{
37 BindingOriginEvidenceV0, CertaintyReasonEvidenceV0, ReferenceSiteIdentityEvidenceV0,
38 SourceInputPromotionEvidenceSummaryV0, StyleModuleEdgeEvidenceV0,
39 ValueDomainExplanationEvidenceV0, summarize_omena_bridge_source_input_evidence,
40};
41pub use source_imports::{
42 SourceImportDeclarationSummaryV0, SourceImportDeclarationV0,
43 summarize_omena_bridge_source_import_declarations,
44 summarize_omena_bridge_source_import_declarations_for_path,
45 summarize_omena_bridge_source_import_declarations_for_source_language,
46};
47pub use source_syntax::{
48 SourceImportedStyleBindingV0, SourceSelectorReferenceFactV0,
49 SourceSelectorReferenceMatchKindV0, SourceStylePropertyAccessFactV0, SourceSyntaxIndexV0,
50 SourceTypeFactTargetV0, canonicalize_source_selector_references,
51 collect_omena_bridge_vue_style_module_bindings, summarize_omena_bridge_source_syntax_index,
52 summarize_omena_bridge_source_syntax_index_for_source_language,
53};
54pub use style_resolution::{
55 OmenaBridgeStyleResolutionInputsV0, OmenaBridgeStyleResolutionSummaryV0,
56 generate_omena_bridge_sif_for_resolved_style_path,
57 load_omena_bridge_workspace_style_resolution_inputs,
58 resolve_omena_bridge_style_uri_for_specifier,
59 resolve_omena_bridge_style_uri_for_specifier_with_package_manifests,
60 resolve_omena_bridge_style_uri_for_specifier_with_resolution_inputs,
61 summarize_omena_bridge_style_resolution_boundary,
62};
63
64pub fn collect_omena_bridge_design_token_workspace_declarations(
65 style_path: &str,
66 sheet: &Stylesheet,
67) -> Vec<DesignTokenWorkspaceDeclarationFactV0> {
68 let parser_facts = omena_semantic::summarize_parser_contract_facts(sheet);
69 omena_semantic::collect_design_token_workspace_declarations(style_path, &parser_facts)
70}
71
72pub fn collect_omena_bridge_design_token_workspace_declarations_from_source(
73 style_path: &str,
74 style_source: &str,
75) -> Vec<DesignTokenWorkspaceDeclarationFactV0> {
76 let boundary = omena_semantic::summarize_omena_parser_style_semantic_boundary_from_source(
77 style_path,
78 style_source,
79 );
80 omena_semantic::collect_design_token_workspace_declarations(style_path, &boundary.parser_facts)
81}
82
83#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
84#[serde(rename_all = "camelCase")]
85pub struct OmenaBridgeBoundarySummaryV0 {
86 pub schema_version: &'static str,
87 pub product: &'static str,
88 pub bridge_name: &'static str,
89 pub graph_product: &'static str,
90 pub delegated_semantic_boundary_product: &'static str,
91 pub selector_reference_product: &'static str,
92 pub source_input_evidence_product: &'static str,
93 pub binder_plugin_product: &'static str,
94 pub bridge_owned_surfaces: Vec<&'static str>,
95 pub cme_coupled_surfaces: Vec<&'static str>,
96 pub next_decoupling_targets: Vec<&'static str>,
97}
98
99#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
100#[serde(rename_all = "camelCase")]
101pub struct BinderPluginBoundarySummaryV0 {
102 pub schema_version: &'static str,
103 pub product: &'static str,
104 pub owner_crate: &'static str,
105 pub contract_name: &'static str,
106 pub external_plugin_abi_stable: bool,
107 pub default_plugin: BinderPluginSummaryV0,
108 pub built_in_plugins: Vec<BinderPluginSummaryV0>,
109 pub request_path_policy: Vec<&'static str>,
110 pub next_plugin_targets: Vec<&'static str>,
111}
112
113#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
114#[serde(rename_all = "camelCase")]
115pub struct BinderPluginSummaryV0 {
116 pub id: &'static str,
117 pub version: &'static str,
118 pub stability: &'static str,
119 pub domains: Vec<&'static str>,
120 pub owns_surfaces: Vec<&'static str>,
121 pub import_targets: Vec<&'static str>,
122 pub utility_targets: Vec<&'static str>,
123}
124
125#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
126#[serde(rename_all = "camelCase")]
127pub struct StyleSemanticGraphSummaryV0 {
128 pub schema_version: &'static str,
129 pub product: &'static str,
130 pub language: &'static str,
131 pub parser_facts: ParserBoundarySyntaxFactsV0,
132 pub semantic_facts: StyleSemanticFactsV0,
133 pub css_modules_semantics: CssModulesSemanticSummaryV0,
134 pub design_token_semantics: DesignTokenSemanticSummaryV0,
135 pub selector_identity_engine: SelectorIdentityEngineSummaryV0,
136 pub selector_reference_engine: SelectorReferenceEngineSummaryV0,
137 pub source_input_evidence: SourceInputPromotionEvidenceSummaryV0,
138 pub promotion_evidence: SemanticPromotionEvidenceSummaryV0,
139 pub lossless_cst_contract: LosslessCstContractV0,
140}
141
142pub fn summarize_omena_bridge_boundary() -> OmenaBridgeBoundarySummaryV0 {
143 OmenaBridgeBoundarySummaryV0 {
144 schema_version: "0",
145 product: "omena-bridge.cme-semantic-bridge",
146 bridge_name: "cme-semantic-bridge",
147 graph_product: "omena-semantic.style-semantic-graph",
148 delegated_semantic_boundary_product: "omena-semantic.style-semantic-boundary",
149 selector_reference_product: "omena-semantic.selector-references",
150 source_input_evidence_product: "omena-semantic.source-input-evidence",
151 binder_plugin_product: "omena-bridge.binder-plugin-boundary",
152 bridge_owned_surfaces: vec![
153 "styleSemanticGraph",
154 "styleSemanticGraphFromSource",
155 "omenaParserBackedStyleSemanticBoundaryFromSource",
156 "selectorReferenceEngine",
157 "designTokenWorkspaceDeclarationsFromSource",
158 "sourceInputEvidence",
159 "sourceImportDeclarations",
160 "styleResolution",
161 "sourceSyntaxIndex",
162 "promotionEvidenceWithSourceInput",
163 "binderPluginBoundary",
164 ],
165 cme_coupled_surfaces: vec![
166 "EngineInputV2",
167 "sourceInputEvidence",
168 "selectorReferenceEngine",
169 "promotionEvidenceWithSourceInput",
170 "styleSemanticGraphFromSource",
171 "cssModulesClassnameBinding",
172 "sourceSyntaxIndex",
173 "styleResolution",
174 ],
175 next_decoupling_targets: Vec::new(),
176 }
177}
178
179pub fn summarize_omena_bridge_binder_plugin_boundary() -> BinderPluginBoundarySummaryV0 {
180 BinderPluginBoundarySummaryV0 {
181 schema_version: "0",
182 product: "omena-bridge.binder-plugin-boundary",
183 owner_crate: "omena-bridge",
184 contract_name: "BinderPluginV0",
185 external_plugin_abi_stable: false,
186 default_plugin: BinderPluginSummaryV0 {
187 id: "css-modules-classnames-bind",
188 version: "0",
189 stability: "builtIn",
190 domains: vec!["css-modules"],
191 owns_surfaces: vec![
192 "styleImportRecognition",
193 "classUtilityRecognition",
194 "classReferenceExtraction",
195 "sourceExpressionProjection",
196 ],
197 import_targets: vec!["*.module.css", "*.module.scss", "*.module.less"],
198 utility_targets: vec!["classnames/bind", "classnames", "clsx", "clsx/lite"],
199 },
200 built_in_plugins: vec![
201 BinderPluginSummaryV0 {
202 id: "css-modules-classnames-bind",
203 version: "0",
204 stability: "builtIn",
205 domains: vec!["css-modules"],
206 owns_surfaces: vec![
207 "styleImportRecognition",
208 "classUtilityRecognition",
209 "classReferenceExtraction",
210 "sourceExpressionProjection",
211 ],
212 import_targets: vec!["*.module.css", "*.module.scss", "*.module.less"],
213 utility_targets: vec!["classnames/bind", "classnames", "clsx", "clsx/lite"],
214 },
215 BinderPluginSummaryV0 {
216 id: "tailwind-uno-utility-domain",
217 version: "0",
218 stability: "builtIn",
219 domains: vec!["tailwind-utilities", "unocss-utilities"],
220 owns_surfaces: vec!["domainClassReferenceExtraction"],
221 import_targets: Vec::new(),
222 utility_targets: vec!["class", "className", "classnames", "clsx", "clsx/lite"],
223 },
224 BinderPluginSummaryV0 {
225 id: "vanilla-extract-recipe-domain",
226 version: "0",
227 stability: "builtIn",
228 domains: vec!["vanilla-extract-recipes"],
229 owns_surfaces: vec!["domainClassReferenceExtraction"],
230 import_targets: vec!["@vanilla-extract/recipes"],
231 utility_targets: vec!["recipe"],
232 },
233 BinderPluginSummaryV0 {
234 id: "vue-style-module-domain",
235 version: "0",
236 stability: "builtIn",
237 domains: vec!["vue-style-modules"],
238 owns_surfaces: vec!["domainClassReferenceExtraction"],
239 import_targets: vec!["*.vue"],
240 utility_targets: vec!["useCssModule"],
241 },
242 ],
243 request_path_policy: vec![
244 "builtInPluginsOnlyUntilAbiStabilizes",
245 "pluginOutputFeedsEngineInputV2",
246 "sourceExpressionProjectionMustPreserveBindingIdentity",
247 "styleImportResolutionMustRemainTargetAware",
248 "styleSourceExtractionIsOptionalForUtilityDomains",
249 ],
250 next_plugin_targets: Vec::new(),
251 }
252}
253
254pub fn summarize_omena_bridge_style_semantic_graph(
255 sheet: &Stylesheet,
256 input: &EngineInputV2,
257) -> StyleSemanticGraphSummaryV0 {
258 summarize_omena_bridge_style_semantic_graph_for_path(sheet, input, None)
259}
260
261pub fn summarize_omena_bridge_style_semantic_graph_for_path(
262 sheet: &Stylesheet,
263 input: &EngineInputV2,
264 style_path: Option<&str>,
265) -> StyleSemanticGraphSummaryV0 {
266 summarize_omena_bridge_style_semantic_graph_for_path_with_workspace_declarations(
267 sheet,
268 input,
269 style_path,
270 &[],
271 )
272}
273
274pub fn summarize_omena_bridge_style_semantic_graph_for_path_with_workspace_declarations(
275 sheet: &Stylesheet,
276 input: &EngineInputV2,
277 style_path: Option<&str>,
278 workspace_declarations: &[DesignTokenWorkspaceDeclarationFactV0],
279) -> StyleSemanticGraphSummaryV0 {
280 summarize_omena_bridge_style_semantic_graph_for_path_with_scoped_workspace_declarations(
281 sheet,
282 input,
283 style_path,
284 workspace_declarations,
285 DesignTokenExternalDeclarationCandidateScopeV0::Workspace,
286 )
287}
288
289pub fn summarize_omena_bridge_style_semantic_graph_for_path_with_scoped_workspace_declarations(
290 sheet: &Stylesheet,
291 input: &EngineInputV2,
292 style_path: Option<&str>,
293 workspace_declarations: &[DesignTokenWorkspaceDeclarationFactV0],
294 candidate_scope: DesignTokenExternalDeclarationCandidateScopeV0,
295) -> StyleSemanticGraphSummaryV0 {
296 let boundary = omena_semantic::summarize_style_semantic_boundary(sheet);
297 let css_modules_semantics = omena_semantic::summarize_css_modules_semantics(sheet);
298 summarize_omena_bridge_style_semantic_graph_with_boundary(
299 boundary,
300 css_modules_semantics,
301 input,
302 style_path,
303 workspace_declarations,
304 candidate_scope,
305 )
306}
307
308fn summarize_omena_bridge_style_semantic_graph_with_boundary(
309 boundary: StyleSemanticBoundarySummaryV0,
310 css_modules_semantics: CssModulesSemanticSummaryV0,
311 input: &EngineInputV2,
312 style_path: Option<&str>,
313 workspace_declarations: &[DesignTokenWorkspaceDeclarationFactV0],
314 candidate_scope: DesignTokenExternalDeclarationCandidateScopeV0,
315) -> StyleSemanticGraphSummaryV0 {
316 let parser_facts = boundary.parser_facts;
317 let semantic_facts = boundary.semantic_facts;
318 let design_token_semantics =
319 omena_semantic::summarize_design_token_semantics_with_scoped_workspace_declarations(
320 &parser_facts,
321 &semantic_facts,
322 style_path,
323 workspace_declarations,
324 candidate_scope,
325 );
326 let selector_identity_engine = boundary.selector_identity_engine;
327 let selector_reference_engine =
328 summarize_omena_bridge_selector_reference_engine(input, style_path);
329 let source_input_evidence = summarize_omena_bridge_source_input_evidence(input);
330 let promotion_evidence = summarize_omena_bridge_promotion_evidence_with_source_input(
331 &parser_facts,
332 &semantic_facts,
333 input,
334 );
335 let lossless_cst_contract = boundary.lossless_cst_contract;
336
337 StyleSemanticGraphSummaryV0 {
338 schema_version: "0",
339 product: "omena-semantic.style-semantic-graph",
340 language: boundary.language,
341 parser_facts,
342 semantic_facts,
343 css_modules_semantics,
344 design_token_semantics,
345 selector_identity_engine,
346 selector_reference_engine,
347 source_input_evidence,
348 promotion_evidence,
349 lossless_cst_contract,
350 }
351}
352
353pub fn summarize_omena_bridge_style_semantic_graph_from_source(
354 style_path: &str,
355 style_source: &str,
356 input: &EngineInputV2,
357) -> Option<StyleSemanticGraphSummaryV0> {
358 summarize_omena_bridge_style_semantic_graph_from_source_with_scoped_workspace_declarations(
359 style_path,
360 style_source,
361 input,
362 &[],
363 DesignTokenExternalDeclarationCandidateScopeV0::Workspace,
364 )
365}
366
367pub fn summarize_omena_bridge_style_semantic_graph_from_source_with_scoped_workspace_declarations(
368 style_path: &str,
369 style_source: &str,
370 input: &EngineInputV2,
371 workspace_declarations: &[DesignTokenWorkspaceDeclarationFactV0],
372 candidate_scope: DesignTokenExternalDeclarationCandidateScopeV0,
373) -> Option<StyleSemanticGraphSummaryV0> {
374 let css_modules_semantics =
375 omena_semantic::summarize_css_modules_semantics_from_source(style_path, style_source)?;
376 let boundary = omena_semantic::summarize_omena_parser_style_semantic_boundary_from_source(
377 style_path,
378 style_source,
379 );
380 Some(summarize_omena_bridge_style_semantic_graph_with_boundary(
381 boundary,
382 css_modules_semantics,
383 input,
384 Some(style_path),
385 workspace_declarations,
386 candidate_scope,
387 ))
388}
389
390#[cfg(test)]
391mod tests {
392 use super::{
393 collect_omena_bridge_design_token_workspace_declarations_from_source,
394 summarize_omena_bridge_binder_plugin_boundary, summarize_omena_bridge_boundary,
395 summarize_omena_bridge_promotion_evidence_with_source_input,
396 summarize_omena_bridge_selector_reference_engine,
397 summarize_omena_bridge_source_import_declarations,
398 summarize_omena_bridge_source_input_evidence,
399 summarize_omena_bridge_style_semantic_graph_from_source,
400 };
401 use engine_input_producers::{
402 ClassExpressionInputV2, EngineInputV2, PositionV2, RangeV2, SourceAnalysisInputV2,
403 SourceDocumentV2, StringTypeFactsV2, StyleAnalysisInputV2, StyleDocumentV2,
404 StyleSelectorV2, TypeFactEntryV2,
405 };
406
407 #[test]
408 fn declares_cme_coupled_bridge_boundary() {
409 let boundary = summarize_omena_bridge_boundary();
410
411 assert_eq!(boundary.schema_version, "0");
412 assert_eq!(boundary.product, "omena-bridge.cme-semantic-bridge");
413 assert_eq!(
414 boundary.graph_product,
415 "omena-semantic.style-semantic-graph"
416 );
417 assert_eq!(
418 boundary.delegated_semantic_boundary_product,
419 "omena-semantic.style-semantic-boundary"
420 );
421 assert_eq!(
422 boundary.selector_reference_product,
423 "omena-semantic.selector-references"
424 );
425 assert_eq!(
426 boundary.source_input_evidence_product,
427 "omena-semantic.source-input-evidence"
428 );
429 assert_eq!(
430 boundary.binder_plugin_product,
431 "omena-bridge.binder-plugin-boundary"
432 );
433 assert!(
434 boundary
435 .bridge_owned_surfaces
436 .contains(&"styleSemanticGraphFromSource")
437 );
438 assert!(
439 boundary
440 .bridge_owned_surfaces
441 .contains(&"omenaParserBackedStyleSemanticBoundaryFromSource")
442 );
443 assert!(
444 boundary
445 .bridge_owned_surfaces
446 .contains(&"selectorReferenceEngine")
447 );
448 assert!(
449 boundary
450 .bridge_owned_surfaces
451 .contains(&"designTokenWorkspaceDeclarationsFromSource")
452 );
453 assert!(
454 boundary
455 .bridge_owned_surfaces
456 .contains(&"sourceInputEvidence")
457 );
458 assert!(
459 boundary
460 .bridge_owned_surfaces
461 .contains(&"promotionEvidenceWithSourceInput")
462 );
463 assert!(
464 boundary
465 .bridge_owned_surfaces
466 .contains(&"sourceImportDeclarations")
467 );
468 assert!(boundary.bridge_owned_surfaces.contains(&"styleResolution"));
469 assert!(
470 boundary
471 .bridge_owned_surfaces
472 .contains(&"sourceSyntaxIndex")
473 );
474 assert!(
475 boundary
476 .bridge_owned_surfaces
477 .contains(&"binderPluginBoundary")
478 );
479 assert!(
480 boundary
481 .cme_coupled_surfaces
482 .contains(&"promotionEvidenceWithSourceInput")
483 );
484 assert!(
485 boundary.next_decoupling_targets.is_empty(),
486 "all current omena-bridge decoupling targets should be bridge-owned"
487 );
488 }
489
490 #[test]
491 fn declares_built_in_binder_plugin_boundary() {
492 let boundary = summarize_omena_bridge_binder_plugin_boundary();
493
494 assert_eq!(boundary.schema_version, "0");
495 assert_eq!(boundary.product, "omena-bridge.binder-plugin-boundary");
496 assert_eq!(boundary.contract_name, "BinderPluginV0");
497 assert!(
498 !boundary.external_plugin_abi_stable,
499 "the first boundary cut should not promise a stable external plugin ABI"
500 );
501 assert_eq!(boundary.default_plugin.id, "css-modules-classnames-bind");
502 assert_eq!(boundary.default_plugin.stability, "builtIn");
503 assert!(boundary.default_plugin.domains.contains(&"css-modules"));
504 assert!(
505 boundary
506 .default_plugin
507 .owns_surfaces
508 .contains(&"classReferenceExtraction")
509 );
510 assert!(
511 boundary
512 .default_plugin
513 .utility_targets
514 .contains(&"classnames/bind")
515 );
516 assert!(
517 boundary
518 .request_path_policy
519 .contains(&"pluginOutputFeedsEngineInputV2")
520 );
521 assert!(
522 boundary
523 .request_path_policy
524 .contains(&"styleSourceExtractionIsOptionalForUtilityDomains")
525 );
526 assert!(boundary.built_in_plugins.iter().any(|plugin| {
527 plugin.id == "tailwind-uno-utility-domain"
528 && plugin.domains.contains(&"tailwind-utilities")
529 && plugin
530 .owns_surfaces
531 .contains(&"domainClassReferenceExtraction")
532 }));
533 assert!(
534 !boundary
535 .next_plugin_targets
536 .contains(&"tailwind-utility-domain")
537 );
538 assert!(boundary.built_in_plugins.iter().any(|plugin| {
539 plugin.id == "vanilla-extract-recipe-domain"
540 && plugin.domains.contains(&"vanilla-extract-recipes")
541 && plugin
542 .owns_surfaces
543 .contains(&"domainClassReferenceExtraction")
544 }));
545 assert!(
546 !boundary
547 .next_plugin_targets
548 .contains(&"vanilla-extract-recipe-domain")
549 );
550 assert!(boundary.built_in_plugins.iter().any(|plugin| {
551 plugin.id == "vue-style-module-domain"
552 && plugin.domains.contains(&"vue-style-modules")
553 && plugin
554 .owns_surfaces
555 .contains(&"domainClassReferenceExtraction")
556 }));
557 assert!(
558 boundary.next_plugin_targets.is_empty(),
559 "all planned BinderPluginV0 proof-point domains should now be built in"
560 );
561 }
562
563 #[test]
564 fn summarizes_source_import_declarations_for_css_modules_binding_inputs() {
565 let summary = summarize_omena_bridge_source_import_declarations(
566 r#"
567import bind from "classnames/bind";
568import styles from "./Button.module.scss";
569import * as tokens from "./tokens.module.css";
570import { type BadgeProps } from "./types";
571const lazy = import("./ignored.module.scss");
572"#,
573 );
574
575 assert_eq!(summary.product, "omena-bridge.source-import-declarations");
576 assert_eq!(summary.import_count, 3);
577 assert_eq!(
578 summary
579 .imports
580 .iter()
581 .map(|import| (import.binding.as_str(), import.specifier.as_str()))
582 .collect::<Vec<_>>(),
583 vec![
584 ("bind", "classnames/bind"),
585 ("styles", "./Button.module.scss"),
586 ("tokens", "./tokens.module.css"),
587 ]
588 );
589 }
590
591 #[test]
592 fn exposes_source_input_evidence_through_bridge() {
593 let evidence = summarize_omena_bridge_source_input_evidence(&sample_engine_input());
594
595 assert_eq!(evidence.product, "omena-semantic.source-input-evidence");
596 assert_eq!(evidence.reference_site_identity.status, "ready");
597 assert_eq!(evidence.reference_site_identity.reference_site_count, 1);
598 assert_eq!(evidence.certainty_reason.status, "ready");
599 assert_eq!(evidence.binding_origin.status, "ready");
600 assert!(evidence.blocking_gaps.is_empty());
601 }
602
603 #[test]
604 fn exposes_style_semantic_graph_through_bridge() -> Result<(), String> {
605 let graph = summarize_omena_bridge_style_semantic_graph_from_source(
606 "/tmp/Component.module.scss",
607 ".button { color: red; }",
608 &sample_engine_input(),
609 )
610 .ok_or_else(|| "SCSS module source should parse".to_string())?;
611
612 assert_eq!(graph.product, "omena-semantic.style-semantic-graph");
613 assert_eq!(graph.selector_reference_engine.selector_count, 1);
614 assert_eq!(graph.selector_reference_engine.referenced_selector_count, 1);
615 assert_eq!(
616 graph.source_input_evidence.product,
617 "omena-semantic.source-input-evidence"
618 );
619 assert!(graph.promotion_evidence.blocking_gaps.is_empty());
620 Ok(())
621 }
622
623 #[test]
624 fn exposes_style_semantic_graph_from_source_through_bridge() -> Result<(), String> {
625 let graph = summarize_omena_bridge_style_semantic_graph_from_source(
626 "/tmp/Component.module.scss",
627 r#"@use "./tokens" as tokens; .button { --brand: red; color: var(--brand); color: tokens.$brand; }"#,
628 &sample_engine_input(),
629 )
630 .ok_or_else(|| "bridge should parse SCSS module source".to_string())?;
631
632 assert_eq!(graph.product, "omena-semantic.style-semantic-graph");
633 assert_eq!(
634 graph.parser_facts.custom_properties.decl_names,
635 vec!["--brand".to_string()]
636 );
637 assert_eq!(
638 graph.parser_facts.custom_properties.ref_names,
639 vec!["--brand".to_string()]
640 );
641 assert_eq!(
642 graph.parser_facts.sass.module_use_edges[0].namespace_kind,
643 "alias"
644 );
645 assert_eq!(
646 graph.parser_facts.sass.variable_ref_names,
647 vec!["brand".to_string()]
648 );
649 assert_eq!(
650 graph.selector_reference_engine.style_path,
651 Some("/tmp/Component.module.scss".to_string())
652 );
653 assert_eq!(
654 graph.source_input_evidence.reference_site_identity.status,
655 "ready"
656 );
657 Ok(())
658 }
659
660 #[test]
661 fn collects_design_token_workspace_declarations_from_source_through_bridge() {
662 let declarations = collect_omena_bridge_design_token_workspace_declarations_from_source(
663 "/tmp/tokens.module.scss",
664 r#":root { --brand: red; } .button { --local: blue; color: var(--brand); }"#,
665 );
666
667 assert_eq!(
668 declarations
669 .iter()
670 .map(|declaration| declaration.name.as_str())
671 .collect::<Vec<_>>(),
672 vec!["--brand", "--local"]
673 );
674 assert!(
675 declarations
676 .iter()
677 .all(|declaration| declaration.file_path == "/tmp/tokens.module.scss")
678 );
679 assert_eq!(declarations[0].source_order, 0);
680 assert_eq!(declarations[1].source_order, 1);
681 }
682
683 #[test]
684 fn owns_selector_reference_engine_without_changing_host_product() {
685 let bridge_references = summarize_omena_bridge_selector_reference_engine(
686 &sample_engine_input(),
687 Some("/tmp/Component.module.scss"),
688 );
689 let semantic_references = omena_semantic::summarize_selector_reference_engine(
690 &sample_engine_input(),
691 Some("/tmp/Component.module.scss"),
692 );
693
694 assert_eq!(
695 bridge_references.product,
696 "omena-semantic.selector-references"
697 );
698 assert_eq!(bridge_references.product, semantic_references.product);
699 assert_eq!(bridge_references.style_path, semantic_references.style_path);
700 assert_eq!(
701 bridge_references.selector_count,
702 semantic_references.selector_count
703 );
704 assert_eq!(
705 bridge_references.referenced_selector_count,
706 semantic_references.referenced_selector_count
707 );
708 assert_eq!(
709 bridge_references.total_reference_sites,
710 semantic_references.total_reference_sites
711 );
712 assert_eq!(
713 bridge_references.selectors[0].canonical_id,
714 semantic_references.selectors[0].canonical_id
715 );
716 assert_eq!(
717 bridge_references.selectors[0].editable_direct_reference_count,
718 semantic_references.selectors[0].editable_direct_reference_count
719 );
720 }
721
722 #[test]
723 fn owns_source_input_evidence_without_changing_host_product() {
724 let bridge_evidence = summarize_omena_bridge_source_input_evidence(&sample_engine_input());
725 let semantic_evidence =
726 omena_semantic::summarize_source_input_evidence(&sample_engine_input());
727
728 assert_bridge_source_input_evidence_matches_semantic(&bridge_evidence, &semantic_evidence);
729 }
730
731 #[test]
732 fn owns_source_backed_promotion_evidence_without_changing_host_product() -> Result<(), String> {
733 let boundary = omena_semantic::summarize_omena_parser_style_semantic_boundary_from_source(
734 "/tmp/Component.module.scss",
735 ".button { color: red; }",
736 );
737 let input = sample_engine_input();
738 let bridge_evidence = summarize_omena_bridge_promotion_evidence_with_source_input(
739 &boundary.parser_facts,
740 &boundary.semantic_facts,
741 &input,
742 );
743 let semantic_evidence =
744 omena_semantic::summarize_semantic_promotion_evidence_with_source_input(
745 &boundary.parser_facts,
746 &boundary.semantic_facts,
747 &input,
748 );
749
750 assert_bridge_promotion_evidence_matches_semantic(&bridge_evidence, &semantic_evidence);
751 Ok(())
752 }
753
754 #[test]
755 fn owns_graph_assembly_without_changing_host_product() -> Result<(), String> {
756 let bridge_graph = summarize_omena_bridge_style_semantic_graph_from_source(
757 "/tmp/Component.module.scss",
758 ".button { color: red; }",
759 &sample_engine_input(),
760 )
761 .ok_or_else(|| "bridge should parse SCSS module source".to_string())?;
762 let semantic_graph = omena_semantic::summarize_style_semantic_graph_from_source(
763 "/tmp/Component.module.scss",
764 ".button { color: red; }",
765 &sample_engine_input(),
766 )
767 .ok_or_else(|| "semantic should parse SCSS module source".to_string())?;
768
769 assert_eq!(bridge_graph.product, "omena-semantic.style-semantic-graph");
770 assert_eq!(bridge_graph.product, semantic_graph.product);
771 assert_eq!(bridge_graph.language, semantic_graph.language);
772 assert_eq!(
773 bridge_graph.selector_reference_engine.product,
774 semantic_graph.selector_reference_engine.product
775 );
776 assert_eq!(
777 bridge_graph.selector_reference_engine.selector_count,
778 semantic_graph.selector_reference_engine.selector_count
779 );
780 assert_eq!(
781 bridge_graph.selector_reference_engine.total_reference_sites,
782 semantic_graph
783 .selector_reference_engine
784 .total_reference_sites
785 );
786 assert_bridge_source_input_evidence_matches_semantic(
787 &bridge_graph.source_input_evidence,
788 &semantic_graph.source_input_evidence,
789 );
790 assert_eq!(
791 bridge_graph.design_token_semantics,
792 semantic_graph.design_token_semantics
793 );
794 assert_bridge_promotion_evidence_matches_semantic(
795 &bridge_graph.promotion_evidence,
796 &semantic_graph.promotion_evidence,
797 );
798 Ok(())
799 }
800
801 fn assert_bridge_promotion_evidence_matches_semantic(
802 bridge: &super::SemanticPromotionEvidenceSummaryV0,
803 semantic: &omena_semantic::SemanticPromotionEvidenceSummaryV0,
804 ) {
805 assert_eq!(bridge.schema_version, semantic.schema_version);
806 assert_eq!(bridge.product, semantic.product);
807 assert_eq!(bridge.blocking_gaps, semantic.blocking_gaps);
808 assert_eq!(bridge.next_priorities, semantic.next_priorities);
809 assert_eq!(bridge.items.len(), semantic.items.len());
810
811 for (bridge_item, semantic_item) in bridge.items.iter().zip(&semantic.items) {
812 assert_eq!(bridge_item.evidence, semantic_item.evidence);
813 assert_eq!(bridge_item.status, semantic_item.status);
814 assert_eq!(bridge_item.provider, semantic_item.provider);
815 assert_eq!(bridge_item.observed_count, semantic_item.observed_count);
816 assert_eq!(bridge_item.reason, semantic_item.reason);
817 }
818 }
819
820 fn assert_bridge_source_input_evidence_matches_semantic(
821 bridge: &super::SourceInputPromotionEvidenceSummaryV0,
822 semantic: &omena_semantic::SourceInputPromotionEvidenceSummaryV0,
823 ) {
824 assert_eq!(bridge.schema_version, semantic.schema_version);
825 assert_eq!(bridge.product, semantic.product);
826 assert_eq!(bridge.input_version, semantic.input_version);
827 assert_eq!(
828 bridge.reference_site_identity.status,
829 semantic.reference_site_identity.status
830 );
831 assert_eq!(
832 bridge.reference_site_identity.selector_count,
833 semantic.reference_site_identity.selector_count
834 );
835 assert_eq!(
836 bridge.reference_site_identity.reference_site_count,
837 semantic.reference_site_identity.reference_site_count
838 );
839 assert_eq!(
840 bridge.reference_site_identity.direct_reference_site_count,
841 semantic.reference_site_identity.direct_reference_site_count
842 );
843 assert_eq!(
844 bridge.reference_site_identity.expanded_reference_site_count,
845 semantic
846 .reference_site_identity
847 .expanded_reference_site_count
848 );
849 assert_eq!(
850 bridge
851 .reference_site_identity
852 .style_dependency_reference_site_count,
853 semantic
854 .reference_site_identity
855 .style_dependency_reference_site_count
856 );
857 assert_eq!(
858 bridge.reference_site_identity.editable_direct_site_count,
859 semantic.reference_site_identity.editable_direct_site_count
860 );
861 assert_eq!(
862 bridge.reference_site_identity.reference_kind_counts,
863 semantic.reference_site_identity.reference_kind_counts
864 );
865 assert_eq!(
866 bridge.certainty_reason.status,
867 semantic.certainty_reason.status
868 );
869 assert_eq!(
870 bridge.certainty_reason.expression_count,
871 semantic.certainty_reason.expression_count
872 );
873 assert_eq!(
874 bridge.certainty_reason.exact_count,
875 semantic.certainty_reason.exact_count
876 );
877 assert_eq!(
878 bridge.certainty_reason.inferred_count,
879 semantic.certainty_reason.inferred_count
880 );
881 assert_eq!(
882 bridge.certainty_reason.possible_count,
883 semantic.certainty_reason.possible_count
884 );
885 assert_eq!(
886 bridge.certainty_reason.missing_reason_count,
887 semantic.certainty_reason.missing_reason_count
888 );
889 assert_eq!(
890 bridge.certainty_reason.reason_counts,
891 semantic.certainty_reason.reason_counts
892 );
893 assert_eq!(
894 bridge.certainty_reason.shape_kind_counts,
895 semantic.certainty_reason.shape_kind_counts
896 );
897 assert_eq!(
898 bridge.certainty_reason.shape_label_counts,
899 semantic.certainty_reason.shape_label_counts
900 );
901 assert_eq!(bridge.binding_origin.status, semantic.binding_origin.status);
902 assert_eq!(
903 bridge.binding_origin.expression_count,
904 semantic.binding_origin.expression_count
905 );
906 assert_eq!(
907 bridge.binding_origin.direct_class_name_count,
908 semantic.binding_origin.direct_class_name_count
909 );
910 assert_eq!(
911 bridge.binding_origin.root_binding_count,
912 semantic.binding_origin.root_binding_count
913 );
914 assert_eq!(
915 bridge.binding_origin.access_path_count,
916 semantic.binding_origin.access_path_count
917 );
918 assert_eq!(
919 bridge.binding_origin.access_path_segment_count,
920 semantic.binding_origin.access_path_segment_count
921 );
922 assert_eq!(
923 bridge.binding_origin.expression_kind_counts,
924 semantic.binding_origin.expression_kind_counts
925 );
926 assert_eq!(
927 bridge.style_module_edge.status,
928 semantic.style_module_edge.status
929 );
930 assert_eq!(
931 bridge.style_module_edge.source_style_edge_count,
932 semantic.style_module_edge.source_style_edge_count
933 );
934 assert_eq!(
935 bridge.style_module_edge.distinct_style_module_count,
936 semantic.style_module_edge.distinct_style_module_count
937 );
938 assert_eq!(
939 bridge.style_module_edge.missing_style_document_edge_count,
940 semantic.style_module_edge.missing_style_document_edge_count
941 );
942 assert_eq!(
943 bridge.style_module_edge.composed_edge_count,
944 semantic.style_module_edge.composed_edge_count
945 );
946 assert_eq!(
947 bridge.style_module_edge.imported_composed_edge_count,
948 semantic.style_module_edge.imported_composed_edge_count
949 );
950 assert_eq!(
951 bridge.style_module_edge.global_composed_edge_count,
952 semantic.style_module_edge.global_composed_edge_count
953 );
954 assert_eq!(
955 bridge.value_domain_explanation.status,
956 semantic.value_domain_explanation.status
957 );
958 assert_eq!(
959 bridge.value_domain_explanation.expression_count,
960 semantic.value_domain_explanation.expression_count
961 );
962 assert_eq!(
963 bridge.value_domain_explanation.exact_expression_count,
964 semantic.value_domain_explanation.exact_expression_count
965 );
966 assert_eq!(
967 bridge
968 .value_domain_explanation
969 .finite_value_expression_count,
970 semantic
971 .value_domain_explanation
972 .finite_value_expression_count
973 );
974 assert_eq!(
975 bridge.value_domain_explanation.constrained_expression_count,
976 semantic
977 .value_domain_explanation
978 .constrained_expression_count
979 );
980 assert_eq!(
981 bridge.value_domain_explanation.unknown_expression_count,
982 semantic.value_domain_explanation.unknown_expression_count
983 );
984 assert_eq!(
985 bridge.value_domain_explanation.finite_value_count,
986 semantic.value_domain_explanation.finite_value_count
987 );
988 assert_eq!(
989 bridge.value_domain_explanation.derivation_count,
990 semantic.value_domain_explanation.derivation_count
991 );
992 assert_eq!(
993 bridge.value_domain_explanation.derivation_step_count,
994 semantic.value_domain_explanation.derivation_step_count
995 );
996 assert_eq!(
997 bridge.value_domain_explanation.value_domain_kind_counts,
998 semantic.value_domain_explanation.value_domain_kind_counts
999 );
1000 assert_eq!(
1001 bridge.value_domain_explanation.constraint_kind_counts,
1002 semantic.value_domain_explanation.constraint_kind_counts
1003 );
1004 assert_eq!(
1005 bridge.value_domain_explanation.derivation_product_counts,
1006 semantic.value_domain_explanation.derivation_product_counts
1007 );
1008 assert_eq!(
1009 bridge
1010 .value_domain_explanation
1011 .derivation_reduced_kind_counts,
1012 semantic
1013 .value_domain_explanation
1014 .derivation_reduced_kind_counts
1015 );
1016 assert_eq!(
1017 bridge.value_domain_explanation.derivation_operation_counts,
1018 semantic
1019 .value_domain_explanation
1020 .derivation_operation_counts
1021 );
1022 assert_eq!(bridge.blocking_gaps, semantic.blocking_gaps);
1023 assert_eq!(bridge.next_priorities, semantic.next_priorities);
1024 }
1025
1026 fn sample_engine_input() -> EngineInputV2 {
1027 EngineInputV2 {
1028 version: "2".to_string(),
1029 sources: vec![SourceAnalysisInputV2 {
1030 document: SourceDocumentV2 {
1031 class_expressions: vec![ClassExpressionInputV2 {
1032 id: "expr-literal".to_string(),
1033 kind: "literal".to_string(),
1034 scss_module_path: "/tmp/Component.module.scss".to_string(),
1035 range: range(4, 12, 4, 18),
1036 class_name: Some("button".to_string()),
1037 root_binding_decl_id: None,
1038 access_path: None,
1039 }],
1040 },
1041 }],
1042 styles: vec![StyleAnalysisInputV2 {
1043 file_path: "/tmp/Component.module.scss".to_string(),
1044 source: None,
1045 document: StyleDocumentV2 {
1046 selectors: vec![StyleSelectorV2 {
1047 name: "button".to_string(),
1048 view_kind: "canonical".to_string(),
1049 canonical_name: Some("button".to_string()),
1050 range: range(0, 1, 0, 7),
1051 nested_safety: Some("flat".to_string()),
1052 composes: None,
1053 bem_suffix: None,
1054 }],
1055 },
1056 }],
1057 type_facts: vec![TypeFactEntryV2 {
1058 file_path: "/tmp/Component.tsx".to_string(),
1059 expression_id: "expr-literal".to_string(),
1060 facts: StringTypeFactsV2 {
1061 kind: "exact".to_string(),
1062 constraint_kind: None,
1063 values: Some(vec!["button".to_string()]),
1064 prefix: None,
1065 suffix: None,
1066 min_len: None,
1067 max_len: None,
1068 char_must: None,
1069 char_may: None,
1070 may_include_other_chars: None,
1071 },
1072 }],
1073 }
1074 }
1075
1076 fn range(
1077 start_line: usize,
1078 start_character: usize,
1079 end_line: usize,
1080 end_character: usize,
1081 ) -> RangeV2 {
1082 RangeV2 {
1083 start: PositionV2 {
1084 line: start_line,
1085 character: start_character,
1086 },
1087 end: PositionV2 {
1088 line: end_line,
1089 character: end_character,
1090 },
1091 }
1092 }
1093}