Skip to main content

omena_query/style/
transform.rs

1use super::*;
2
3use super::parser_facade::parse_omena_query_omena_parser_style_source;
4
5mod context;
6mod css_modules;
7mod design_tokens;
8mod imports;
9mod static_stylesheet;
10
11use context::{
12    find_target_style_source, merge_target_options_transform_context, merge_transform_context,
13};
14use css_modules::{
15    derive_class_name_rewrites_for_transform_context,
16    derive_css_module_composes_resolutions_for_transform_context,
17    derive_css_module_value_resolutions_for_transform_context,
18};
19use design_tokens::derive_design_token_routes_for_transform_context;
20use imports::derive_import_inlines_for_transform_context;
21use static_stylesheet::{
22    derive_static_scss_module_use_evaluations_for_transform_context,
23    derive_static_stylesheet_module_evaluation_for_transform_context,
24};
25
26pub fn summarize_omena_query_transform_plan_from_source(
27    style_path: &str,
28    style_source: &str,
29    target_label: &str,
30    target_support: OmenaQueryTargetFeatureSupportV0,
31    target_options: OmenaQueryTargetTransformOptionsV0,
32    print_options: OmenaQueryTransformPrintOptionsV0,
33) -> OmenaQueryTransformPlanSummaryV0 {
34    summarize_omena_query_transform_plan_from_source_with_context(
35        style_path,
36        style_source,
37        target_label,
38        target_support,
39        target_options,
40        print_options,
41        &TransformExecutionContextV0::default(),
42    )
43}
44
45pub fn summarize_omena_query_transform_plan_from_source_with_context(
46    style_path: &str,
47    style_source: &str,
48    target_label: &str,
49    target_support: OmenaQueryTargetFeatureSupportV0,
50    target_options: OmenaQueryTargetTransformOptionsV0,
51    print_options: OmenaQueryTransformPrintOptionsV0,
52    context: &TransformExecutionContextV0,
53) -> OmenaQueryTransformPlanSummaryV0 {
54    let dialect = omena_parser_dialect_for_style_path(style_path);
55    let bundle = summarize_omena_transform_bundle_from_source(style_path, style_source, dialect);
56    let target = plan_target_transforms(target_label, target_support, target_options);
57    let execution_context = merge_target_options_transform_context(context, target_options);
58    summarize_omena_query_transform_plan_from_parts(TransformPlanPartsV0 {
59        style_path,
60        style_source,
61        dialect,
62        bundle,
63        target,
64        target_query: None,
65        print_options,
66        context: &execution_context,
67    })
68}
69
70pub fn summarize_omena_query_transform_plan_from_target_query(
71    style_path: &str,
72    style_source: &str,
73    target_query: &str,
74    target_options: OmenaQueryTargetTransformOptionsV0,
75    print_options: OmenaQueryTransformPrintOptionsV0,
76) -> OmenaQueryTransformPlanSummaryV0 {
77    summarize_omena_query_transform_plan_from_target_query_with_context(
78        style_path,
79        style_source,
80        target_query,
81        target_options,
82        print_options,
83        &TransformExecutionContextV0::default(),
84    )
85}
86
87pub fn summarize_omena_query_transform_plan_from_target_query_with_context(
88    style_path: &str,
89    style_source: &str,
90    target_query: &str,
91    target_options: OmenaQueryTargetTransformOptionsV0,
92    print_options: OmenaQueryTransformPrintOptionsV0,
93    context: &TransformExecutionContextV0,
94) -> OmenaQueryTransformPlanSummaryV0 {
95    let dialect = omena_parser_dialect_for_style_path(style_path);
96    let bundle = summarize_omena_transform_bundle_from_source(style_path, style_source, dialect);
97    let target_query_plan = plan_target_transforms_from_query(target_query, target_options);
98    let target = target_query_plan.transform_plan.clone();
99    let execution_context = merge_target_options_transform_context(context, target_options);
100    summarize_omena_query_transform_plan_from_parts(TransformPlanPartsV0 {
101        style_path,
102        style_source,
103        dialect,
104        bundle,
105        target,
106        target_query: Some(target_query_plan),
107        print_options,
108        context: &execution_context,
109    })
110}
111
112struct TransformPlanPartsV0<'a> {
113    style_path: &'a str,
114    style_source: &'a str,
115    dialect: OmenaParserStyleDialect,
116    bundle: TransformBundleSourceSummaryV0,
117    target: TransformTargetPlanV0,
118    target_query: Option<OmenaQueryTransformTargetQueryPlanV0>,
119    print_options: OmenaQueryTransformPrintOptionsV0,
120    context: &'a TransformExecutionContextV0,
121}
122
123fn summarize_omena_query_transform_plan_from_parts(
124    parts: TransformPlanPartsV0<'_>,
125) -> OmenaQueryTransformPlanSummaryV0 {
126    let egg = plan_egg_rewrite_passes_for_source(parts.style_source);
127    let custom_property_fixed_point = summarize_static_css_custom_property_fixed_point_from_source(
128        parts.style_source,
129        parts.dialect,
130    );
131
132    let mut combined_passes = Vec::new();
133    extend_passes_from_ids(&parts.bundle.planned_pass_ids, &mut combined_passes);
134    extend_passes_from_ids(&parts.target.planned_pass_ids, &mut combined_passes);
135    extend_passes_from_ids(&egg.planned_pass_ids, &mut combined_passes);
136    combined_passes.push(TransformPassKind::PrintCss);
137    combined_passes.sort_by_key(|pass| pass.ordinal());
138    combined_passes.dedup();
139
140    let combined_plan = plan_transform_passes(&combined_passes);
141    let semantic_signature = format!(
142        "omena-query-transform:{}:{}",
143        parts.style_path,
144        parts.style_source.len()
145    );
146    let execution = execute_transform_passes_on_source_with_dialect_and_context(
147        parts.style_source,
148        parts.dialect,
149        &combined_passes,
150        parts.context,
151    );
152    let print = print_transform_execution_artifact_with_dialect_and_source(
153        parts.style_path,
154        parts.style_source,
155        parts.dialect,
156        semantic_signature,
157        &combined_passes,
158        parts.print_options,
159        &execution,
160    );
161    let egg_witnesses = execute_egg_rewrite_witnesses_for_css_source(
162        parts.style_source,
163        &execution.output_css,
164        &egg.planned_pass_ids,
165    );
166    let semantic_removal_count = execution.semantic_removals.len();
167    let combined_pass_ids = combined_plan.ordered_pass_ids.clone();
168    let combined_violated_dag_edge_count = combined_plan.violated_dag_edge_count;
169
170    OmenaQueryTransformPlanSummaryV0 {
171        schema_version: "0",
172        product: "omena-query.transform-plan",
173        style_path: parts.style_path.to_string(),
174        dialect: omena_parser_style_dialect_label(parts.dialect),
175        bundle: parts.bundle,
176        target: parts.target,
177        target_query: parts.target_query,
178        egg,
179        egg_witnesses,
180        custom_property_fixed_point,
181        print,
182        execution,
183        semantic_removal_count,
184        combined_plan,
185        combined_pass_ids,
186        combined_violated_dag_edge_count,
187        ready_surfaces: vec![
188            "transformBundlePlan",
189            "transformTargetPlan",
190            "transformEggPlan",
191            "transformEggExecutionWitnesses",
192            "customPropertyLeastFixedPoint",
193            "transformPrintArtifact",
194            "transformExecutionRuntime",
195            "cascadeProofObligations",
196            "combinedTransformPassPlan",
197        ],
198    }
199}
200
201pub fn execute_omena_query_transform_passes_from_source(
202    style_path: &str,
203    style_source: &str,
204    requested_pass_ids: &[String],
205) -> OmenaQueryTransformExecuteSummaryV0 {
206    execute_omena_query_transform_passes_from_source_with_context(
207        style_path,
208        style_source,
209        requested_pass_ids,
210        &TransformExecutionContextV0::default(),
211    )
212}
213
214pub fn summarize_omena_query_consumer_check_style_source(
215    style_path: &str,
216    style_source: &str,
217) -> OmenaQueryConsumerCheckSummaryV0 {
218    let dialect = omena_parser_dialect_for_style_path(style_path);
219    let parse_result = parse_omena_query_omena_parser_style_source(style_source, dialect);
220    let style_facts = summarize_omena_query_omena_parser_style_facts(style_source, dialect);
221
222    OmenaQueryConsumerCheckSummaryV0 {
223        schema_version: "0",
224        product: "omena-query.consumer-check-style-source",
225        style_path: style_path.to_string(),
226        dialect: omena_parser_style_dialect_label(dialect),
227        token_count: parse_result.token_count(),
228        parser_error_count: parse_result.errors().len(),
229        class_selector_count: style_facts.class_selector_names.len(),
230        custom_property_count: style_facts.custom_property_names.len(),
231        keyframe_count: style_facts.keyframe_names.len(),
232        ready_surfaces: vec![
233            "consumerCheckFacade",
234            "parserFactSummary",
235            "styleDocumentDiagnostics",
236        ],
237    }
238}
239
240pub fn execute_omena_query_consumer_build_style_source(
241    style_path: &str,
242    style_source: &str,
243    requested_pass_ids: &[String],
244) -> OmenaQueryConsumerBuildSummaryV0 {
245    execute_omena_query_consumer_build_style_source_with_context(
246        style_path,
247        style_source,
248        requested_pass_ids,
249        &TransformExecutionContextV0::default(),
250    )
251}
252
253pub fn execute_omena_query_consumer_build_style_source_with_context(
254    style_path: &str,
255    style_source: &str,
256    requested_pass_ids: &[String],
257    context: &TransformExecutionContextV0,
258) -> OmenaQueryConsumerBuildSummaryV0 {
259    let pass_ids = if requested_pass_ids.is_empty() {
260        all_transform_pass_kinds()
261            .into_iter()
262            .map(|pass| pass.id().to_string())
263            .collect::<Vec<_>>()
264    } else {
265        requested_pass_ids.to_vec()
266    };
267    let context = merge_single_source_transform_context(style_path, style_source, context);
268    let execution_summary = execute_omena_query_transform_passes_from_source_with_context(
269        style_path,
270        style_source,
271        &pass_ids,
272        &context,
273    );
274
275    OmenaQueryConsumerBuildSummaryV0 {
276        schema_version: "0",
277        product: "omena-query.consumer-build-style-source",
278        style_path: style_path.to_string(),
279        dialect: omena_parser_style_dialect_label(omena_parser_dialect_for_style_path(style_path)),
280        requested_pass_ids: requested_pass_ids.to_vec(),
281        target_query: None,
282        unknown_pass_ids: execution_summary.unknown_pass_ids,
283        semantic_removal_count: execution_summary.semantic_removal_count,
284        execution: execution_summary.execution,
285        ready_surfaces: vec![
286            "consumerBuildFacade",
287            "singleSourceTransformContextProducer",
288            "transformExecutionRuntime",
289            "transformPassOutcomeContract",
290        ],
291    }
292}
293
294pub fn execute_omena_query_consumer_build_style_source_with_engine_input_context(
295    style_path: &str,
296    style_source: &str,
297    requested_pass_ids: &[String],
298    input: &EngineInputV2,
299    closed_style_world: bool,
300) -> OmenaQueryConsumerBuildSummaryV0 {
301    let context_summary = summarize_omena_query_transform_context_from_engine_input(
302        input,
303        style_path,
304        closed_style_world,
305    );
306    let mut summary = execute_omena_query_consumer_build_style_source_with_context(
307        style_path,
308        style_source,
309        requested_pass_ids,
310        &context_summary.context,
311    );
312    summary
313        .ready_surfaces
314        .push("semanticReachabilityTransformContext");
315    summary
316        .ready_surfaces
317        .push("expressionDomainSelectorProjection");
318    summary
319}
320
321pub fn execute_omena_query_consumer_build_style_sources_with_context(
322    target_style_path: &str,
323    style_sources: &[OmenaQueryStyleSourceInputV0],
324    requested_pass_ids: &[String],
325    context: &TransformExecutionContextV0,
326    package_manifests: &[OmenaQueryStylePackageManifestV0],
327) -> Result<OmenaQueryConsumerBuildSummaryV0, String> {
328    let Some(target_source) = find_target_style_source(target_style_path, style_sources) else {
329        return Err(format!(
330            "target style path {target_style_path:?} was not found in workspace style sources"
331        ));
332    };
333    let context = merge_workspace_transform_context(
334        target_style_path,
335        style_sources,
336        context,
337        package_manifests,
338    );
339    let mut summary = execute_omena_query_consumer_build_style_source_with_context(
340        target_style_path,
341        target_source,
342        requested_pass_ids,
343        &context,
344    );
345    summary
346        .ready_surfaces
347        .push("multiSourceTransformContextProducer");
348    Ok(summary)
349}
350
351pub fn execute_omena_query_consumer_build_style_sources(
352    target_style_path: &str,
353    style_sources: &[OmenaQueryStyleSourceInputV0],
354    requested_pass_ids: &[String],
355    package_manifests: &[OmenaQueryStylePackageManifestV0],
356) -> Result<OmenaQueryConsumerBuildSummaryV0, String> {
357    execute_omena_query_consumer_build_style_sources_with_context(
358        target_style_path,
359        style_sources,
360        requested_pass_ids,
361        &TransformExecutionContextV0::default(),
362        package_manifests,
363    )
364}
365
366pub fn execute_omena_query_consumer_build_style_source_for_target_query(
367    style_path: &str,
368    style_source: &str,
369    target_query: &str,
370) -> OmenaQueryConsumerBuildSummaryV0 {
371    execute_omena_query_consumer_build_style_source_for_target_query_with_options(
372        style_path,
373        style_source,
374        target_query,
375        conservative_omena_query_target_options(),
376    )
377}
378
379pub fn execute_omena_query_consumer_build_style_source_for_target_query_with_options(
380    style_path: &str,
381    style_source: &str,
382    target_query: &str,
383    target_options: OmenaQueryTargetTransformOptionsV0,
384) -> OmenaQueryConsumerBuildSummaryV0 {
385    execute_omena_query_consumer_build_style_source_for_target_query_with_context_and_options(
386        style_path,
387        style_source,
388        target_query,
389        &TransformExecutionContextV0::default(),
390        target_options,
391    )
392}
393
394pub fn execute_omena_query_consumer_build_style_source_for_target_query_with_context_and_options(
395    style_path: &str,
396    style_source: &str,
397    target_query: &str,
398    context: &TransformExecutionContextV0,
399    target_options: OmenaQueryTargetTransformOptionsV0,
400) -> OmenaQueryConsumerBuildSummaryV0 {
401    let context = merge_single_source_transform_context(style_path, style_source, context);
402    let plan = summarize_omena_query_transform_plan_from_target_query_with_context(
403        style_path,
404        style_source,
405        target_query,
406        target_options,
407        default_omena_query_transform_print_options(),
408        &context,
409    );
410    let requested_pass_ids = plan
411        .combined_pass_ids
412        .iter()
413        .map(|pass_id| (*pass_id).to_string())
414        .collect::<Vec<_>>();
415
416    OmenaQueryConsumerBuildSummaryV0 {
417        schema_version: "0",
418        product: "omena-query.consumer-build-style-source",
419        style_path: plan.style_path,
420        dialect: plan.dialect,
421        requested_pass_ids,
422        target_query: plan.target_query,
423        unknown_pass_ids: Vec::new(),
424        semantic_removal_count: plan.semantic_removal_count,
425        execution: plan.execution,
426        ready_surfaces: vec![
427            "consumerBuildFacade",
428            "targetQueryBuildFacade",
429            "singleSourceTransformContextProducer",
430            "transformExecutionRuntime",
431            "transformPassOutcomeContract",
432        ],
433    }
434}
435
436pub fn execute_omena_query_consumer_build_style_sources_for_target_query_with_context_and_options(
437    target_style_path: &str,
438    style_sources: &[OmenaQueryStyleSourceInputV0],
439    target_query: &str,
440    context: &TransformExecutionContextV0,
441    target_options: OmenaQueryTargetTransformOptionsV0,
442    package_manifests: &[OmenaQueryStylePackageManifestV0],
443) -> Result<OmenaQueryConsumerBuildSummaryV0, String> {
444    let Some(target_source) = find_target_style_source(target_style_path, style_sources) else {
445        return Err(format!(
446            "target style path {target_style_path:?} was not found in workspace style sources"
447        ));
448    };
449    let context = merge_workspace_transform_context(
450        target_style_path,
451        style_sources,
452        context,
453        package_manifests,
454    );
455    let mut summary =
456        execute_omena_query_consumer_build_style_source_for_target_query_with_context_and_options(
457            target_style_path,
458            target_source,
459            target_query,
460            &context,
461            target_options,
462        );
463    summary
464        .ready_surfaces
465        .push("multiSourceTransformContextProducer");
466    Ok(summary)
467}
468
469pub fn execute_omena_query_consumer_build_style_sources_for_target_query_with_options(
470    target_style_path: &str,
471    style_sources: &[OmenaQueryStyleSourceInputV0],
472    target_query: &str,
473    target_options: OmenaQueryTargetTransformOptionsV0,
474    package_manifests: &[OmenaQueryStylePackageManifestV0],
475) -> Result<OmenaQueryConsumerBuildSummaryV0, String> {
476    execute_omena_query_consumer_build_style_sources_for_target_query_with_context_and_options(
477        target_style_path,
478        style_sources,
479        target_query,
480        &TransformExecutionContextV0::default(),
481        target_options,
482        package_manifests,
483    )
484}
485
486fn derive_single_source_transform_context(
487    style_path: &str,
488    style_source: &str,
489) -> TransformExecutionContextV0 {
490    summarize_omena_query_transform_context_from_sources(
491        style_path,
492        [(style_path, style_source)],
493        &[],
494    )
495    .context
496}
497
498fn merge_single_source_transform_context(
499    style_path: &str,
500    style_source: &str,
501    context: &TransformExecutionContextV0,
502) -> TransformExecutionContextV0 {
503    merge_transform_context(
504        derive_single_source_transform_context(style_path, style_source),
505        context,
506    )
507}
508
509fn merge_workspace_transform_context(
510    target_style_path: &str,
511    style_sources: &[OmenaQueryStyleSourceInputV0],
512    context: &TransformExecutionContextV0,
513    package_manifests: &[OmenaQueryStylePackageManifestV0],
514) -> TransformExecutionContextV0 {
515    let style_refs = style_sources
516        .iter()
517        .map(|source| (source.style_path.as_str(), source.style_source.as_str()))
518        .collect::<Vec<_>>();
519    let derived = summarize_omena_query_transform_context_from_sources(
520        target_style_path,
521        style_refs,
522        package_manifests,
523    )
524    .context;
525    merge_transform_context(derived, context)
526}
527
528pub fn summarize_omena_query_transform_context_from_engine_input(
529    input: &EngineInputV2,
530    target_style_path: &str,
531    closed_style_world: bool,
532) -> OmenaQueryTransformContextFromEngineInputSummaryV0 {
533    let projection_summary = summarize_omena_query_expression_domain_selector_projection(input);
534    let mut reachable_class_names = BTreeSet::new();
535    let mut reachability_sources = Vec::new();
536
537    for projection in &projection_summary.projections {
538        if projection.target_style_paths.is_empty()
539            || projection
540                .target_style_paths
541                .iter()
542                .any(|path| path == target_style_path)
543        {
544            reachable_class_names.extend(projection.selector_names.iter().cloned());
545            reachability_sources.push(OmenaQuerySemanticReachabilitySourceV0 {
546                graph_id: projection.graph_id.clone(),
547                file_path: projection.file_path.clone(),
548                node_id: projection.node_id.clone(),
549                target_style_paths: projection.target_style_paths.clone(),
550                value_kind: projection.value_kind,
551                reduced_product: projection.reduced_product.clone(),
552                selector_names: projection.selector_names.clone(),
553                certainty: projection.certainty,
554            });
555        }
556    }
557
558    let semantic_context = TransformExecutionContextV0 {
559        closed_style_world,
560        reachable_class_names: reachable_class_names.into_iter().collect(),
561        ..TransformExecutionContextV0::default()
562    };
563    let style_sources = input
564        .styles
565        .iter()
566        .filter_map(|style| {
567            style
568                .source
569                .as_deref()
570                .map(|source| (style.file_path.as_str(), source))
571        })
572        .collect::<Vec<_>>();
573    let source_context_summary = (!style_sources.is_empty()).then(|| {
574        summarize_omena_query_transform_context_from_sources(target_style_path, style_sources, &[])
575    });
576    let context = if let Some(source_context_summary) = &source_context_summary {
577        merge_transform_context(source_context_summary.context.clone(), &semantic_context)
578    } else {
579        semantic_context
580    };
581
582    let mut ready_surfaces = vec![
583        "expressionDomainSelectorProjection",
584        "semanticReachabilityTransformContext",
585    ];
586    if source_context_summary.is_some() {
587        ready_surfaces.push("engineInputStyleSourceTransformContext");
588    }
589
590    OmenaQueryTransformContextFromEngineInputSummaryV0 {
591        schema_version: "0",
592        product: "omena-query.transform-context-from-engine-input",
593        input_version: input.version.clone(),
594        target_style_path: target_style_path.to_string(),
595        closed_style_world,
596        style_source_count: source_context_summary
597            .as_ref()
598            .map_or(0, |summary| summary.style_count),
599        projection_count: projection_summary.projection_count,
600        selected_projection_count: reachability_sources.len(),
601        import_inline_count: context.import_inlines.len(),
602        class_name_rewrite_count: context.class_name_rewrites.len(),
603        css_module_composes_resolution_count: context.css_module_composes_resolutions.len(),
604        css_module_value_resolution_count: context.css_module_value_resolutions.len(),
605        design_token_route_count: context.design_token_routes.len(),
606        reachable_class_name_count: context.reachable_class_names.len(),
607        reachable_keyframe_name_count: context.reachable_keyframe_names.len(),
608        reachable_value_name_count: context.reachable_value_names.len(),
609        reachable_custom_property_name_count: context.reachable_custom_property_names.len(),
610        reachability_sources,
611        context,
612        ready_surfaces,
613    }
614}
615
616pub fn list_omena_query_transform_pass_summaries() -> Vec<OmenaQueryTransformPassSummaryV0> {
617    all_transform_pass_kinds()
618        .into_iter()
619        .map(|kind| OmenaQueryTransformPassSummaryV0 {
620            id: kind.id(),
621            title: kind.title(),
622            reads_semantic_graph: kind.reads_semantic_graph(),
623            reads_cascade_model: kind.reads_cascade_model(),
624        })
625        .collect()
626}
627
628pub fn execute_omena_query_transform_passes_from_source_with_context(
629    style_path: &str,
630    style_source: &str,
631    requested_pass_ids: &[String],
632    context: &TransformExecutionContextV0,
633) -> OmenaQueryTransformExecuteSummaryV0 {
634    let mut requested_passes = Vec::new();
635    let mut unknown_pass_ids = Vec::new();
636
637    for pass_id in requested_pass_ids {
638        match transform_pass_kind_from_id(pass_id) {
639            Some(pass) => requested_passes.push(pass),
640            None => unknown_pass_ids.push(pass_id.clone()),
641        }
642    }
643
644    let dialect = omena_parser_dialect_for_style_path(style_path);
645    let execution = execute_transform_passes_on_source_with_dialect_and_context(
646        style_source,
647        dialect,
648        &requested_passes,
649        context,
650    );
651    let semantic_removal_count = execution.semantic_removals.len();
652
653    OmenaQueryTransformExecuteSummaryV0 {
654        schema_version: "0",
655        product: "omena-query.transform-execute",
656        style_path: style_path.to_string(),
657        requested_pass_ids: requested_pass_ids.to_vec(),
658        unknown_pass_ids,
659        execution,
660        semantic_removal_count,
661        ready_surfaces: vec!["transformExecutionRuntime", "transformPassOutcomeContract"],
662    }
663}
664
665pub fn summarize_omena_query_transform_context_from_sources<'a>(
666    target_style_path: &str,
667    styles: impl IntoIterator<Item = (&'a str, &'a str)>,
668    package_manifests: &[OmenaQueryStylePackageManifestV0],
669) -> OmenaQueryTransformContextFromSourcesSummaryV0 {
670    let style_sources = styles.into_iter().collect::<Vec<_>>();
671    let style_count = style_sources.len();
672    let style_fact_entries = collect_omena_query_style_fact_entries(style_sources.as_slice());
673    let source_by_path = style_sources
674        .iter()
675        .map(|(style_path, style_source)| ((*style_path).to_string(), (*style_source).to_string()))
676        .collect::<BTreeMap<_, _>>();
677    let available_style_paths = style_fact_entries
678        .iter()
679        .map(|entry| entry.style_path.as_str())
680        .collect::<BTreeSet<_>>();
681    let target_entry = style_fact_entries
682        .iter()
683        .find(|entry| entry.style_path == target_style_path);
684
685    let mut context = TransformExecutionContextV0::default();
686
687    if let Some(entry) = target_entry {
688        context.import_inlines = derive_import_inlines_for_transform_context(
689            entry,
690            &style_fact_entries,
691            &available_style_paths,
692            &source_by_path,
693            package_manifests,
694        );
695        let scss_module_uses = derive_static_scss_module_use_evaluations_for_transform_context(
696            entry,
697            &available_style_paths,
698            &source_by_path,
699            package_manifests,
700        );
701        match omena_parser_dialect_for_style_path(entry.style_path.as_str()) {
702            OmenaParserStyleDialect::Scss | OmenaParserStyleDialect::Sass => {
703                let dialect = omena_parser_dialect_for_style_path(entry.style_path.as_str());
704                context.scss_module_evaluation =
705                    derive_static_stylesheet_module_evaluation_for_transform_context(
706                        entry.style_source.as_str(),
707                        dialect,
708                        &context.import_inlines,
709                        &scss_module_uses,
710                    );
711            }
712            OmenaParserStyleDialect::Less => {
713                context.less_module_evaluation =
714                    derive_static_stylesheet_module_evaluation_for_transform_context(
715                        entry.style_source.as_str(),
716                        OmenaParserStyleDialect::Less,
717                        &context.import_inlines,
718                        &[],
719                    );
720            }
721            OmenaParserStyleDialect::Css => {}
722        }
723        context.class_name_rewrites = derive_class_name_rewrites_for_transform_context(entry);
724        context.css_module_composes_resolutions =
725            derive_css_module_composes_resolutions_for_transform_context(
726                entry,
727                &style_fact_entries,
728                &available_style_paths,
729                package_manifests,
730            );
731        context.css_module_value_resolutions =
732            derive_css_module_value_resolutions_for_transform_context(
733                entry,
734                &style_fact_entries,
735                &available_style_paths,
736                &source_by_path,
737                package_manifests,
738            );
739        context.design_token_routes = derive_design_token_routes_for_transform_context(
740            entry,
741            &style_fact_entries,
742            package_manifests,
743        );
744    }
745
746    OmenaQueryTransformContextFromSourcesSummaryV0 {
747        schema_version: "0",
748        product: "omena-query.transform-context",
749        target_style_path: target_style_path.to_string(),
750        style_count,
751        import_inline_count: context.import_inlines.len(),
752        class_name_rewrite_count: context.class_name_rewrites.len(),
753        css_module_composes_resolution_count: context.css_module_composes_resolutions.len(),
754        css_module_value_resolution_count: context.css_module_value_resolutions.len(),
755        design_token_route_count: context.design_token_routes.len(),
756        reachable_class_name_count: context.reachable_class_names.len(),
757        reachable_keyframe_name_count: context.reachable_keyframe_names.len(),
758        reachable_value_name_count: context.reachable_value_names.len(),
759        reachable_custom_property_name_count: context.reachable_custom_property_names.len(),
760        context,
761        ready_surfaces: vec![
762            "transformContextProducer",
763            "stylesheetModuleEvaluationProducer",
764            "cssModuleClassRewriteProducer",
765            "cssModuleComposesResolutionProducer",
766            "cssModuleValueResolutionProducer",
767            "designTokenRouteProducer",
768            "transitiveImportInlineProducer",
769        ],
770    }
771}
772
773fn apply_transform_source_replacements(
774    source: &str,
775    mut replacements: Vec<(usize, usize, String)>,
776) -> (String, usize) {
777    if replacements.is_empty() {
778        return (source.to_string(), 0);
779    }
780    replacements.sort_by_key(|replacement| replacement.0);
781    let mut output = source.to_string();
782    let mut mutation_count = 0usize;
783    for (start, end, replacement) in replacements.into_iter().rev() {
784        if start > end || end > output.len() {
785            continue;
786        }
787        output.replace_range(start..end, replacement.as_str());
788        mutation_count += 1;
789    }
790    (output, mutation_count)
791}
792
793fn transform_token_start(token: &omena_parser::LexedToken) -> usize {
794    let start: u32 = token.range.start().into();
795    start as usize
796}
797
798fn transform_token_end(token: &omena_parser::LexedToken) -> usize {
799    let end: u32 = token.range.end().into();
800    end as usize
801}
802
803fn extend_passes_from_ids(ids: &[&'static str], passes: &mut Vec<TransformPassKind>) {
804    for candidate in all_transform_pass_kinds() {
805        if ids.contains(&candidate.id()) && !passes.contains(&candidate) {
806            passes.push(candidate);
807        }
808    }
809}
810
811fn transform_pass_kind_from_id(pass_id: &str) -> Option<TransformPassKind> {
812    all_transform_pass_kinds()
813        .into_iter()
814        .find(|candidate| candidate.id() == pass_id)
815}