omena-query 0.3.0

Omena query boundary over CME producer query fragments
Documentation
use super::super::super::parser_facade::summarize_omena_query_omena_parser_style_facts;
use super::super::super::stylesheet_evaluation::{
    derive_static_scss_stylesheet_module_configurable_variable_names,
    derive_static_scss_stylesheet_module_variable_exports,
    derive_static_stylesheet_module_evaluation,
};
use super::super::TransformResolutionContext;
use super::{
    evaluation_source::static_stylesheet_module_output_css_from_evaluation,
    scss_forwarding::{StaticScssModuleForwardEvaluation, inline_static_scss_forward_rules},
    scss_variable_overrides,
};
use crate::OmenaParserStyleDialect;
use std::collections::{BTreeMap, BTreeSet};

#[derive(Debug, Clone)]
pub(super) struct StaticScssModuleContext {
    pub(super) module_output_css: String,
    pub(super) variable_exports: BTreeMap<String, String>,
    configurable_variable_names: BTreeSet<String>,
}

struct StaticScssModuleDeriveContext<'a> {
    available_style_paths: &'a BTreeSet<&'a str>,
    source_by_path: &'a BTreeMap<String, String>,
    resolution_context: TransformResolutionContext<'a>,
    visited: &'a mut BTreeSet<String>,
    emitted_module_identity_keys: &'a mut BTreeSet<String>,
    loaded_module_overrides_by_path: &'a mut BTreeMap<String, BTreeMap<String, String>>,
}

pub(in crate::style::transform) fn derive_static_scss_module_configurable_variable_names_for_transform_context(
    style_path: &str,
    style_source: &str,
    available_style_paths: &BTreeSet<&str>,
    source_by_path: &BTreeMap<String, String>,
    resolution_context: TransformResolutionContext<'_>,
) -> BTreeSet<String> {
    let resolver = StaticScssModuleConfigurableNamesResolver { resolution_context };
    omena_semantic::derive_sass_module_configurable_variable_names(
        style_path,
        style_source,
        available_style_paths,
        source_by_path,
        &resolver,
    )
}

struct StaticScssModuleConfigurableNamesResolver<'a> {
    resolution_context: TransformResolutionContext<'a>,
}

impl omena_semantic::SassModuleConfigurableNamesResolverV0
    for StaticScssModuleConfigurableNamesResolver<'_>
{
    fn local_configurable_names(&self, _style_path: &str, style_source: &str) -> BTreeSet<String> {
        derive_static_scss_stylesheet_module_configurable_variable_names(style_source)
    }

    fn resolve_module_source(
        &self,
        from_style_path: &str,
        source: &str,
        available_style_paths: &BTreeSet<&str>,
    ) -> Option<String> {
        self.resolution_context.resolve_style_module_source(
            from_style_path,
            source,
            available_style_paths,
        )
    }
}

pub(super) struct StaticScssModuleContextRequest<'a> {
    pub(super) style_path: &'a str,
    pub(super) style_source: &'a str,
    pub(super) variable_overrides: &'a BTreeMap<String, String>,
    pub(super) available_style_paths: &'a BTreeSet<&'a str>,
    pub(super) source_by_path: &'a BTreeMap<String, String>,
    pub(super) resolution_context: TransformResolutionContext<'a>,
}

pub(super) fn derive_static_scss_module_context_for_transform_context(
    request: StaticScssModuleContextRequest<'_>,
    emitted_module_identity_keys: &mut BTreeSet<String>,
    loaded_module_overrides_by_path: &mut BTreeMap<String, BTreeMap<String, String>>,
) -> Option<StaticScssModuleContext> {
    let mut visited = BTreeSet::new();
    let mut context = StaticScssModuleDeriveContext {
        available_style_paths: request.available_style_paths,
        source_by_path: request.source_by_path,
        resolution_context: request.resolution_context,
        visited: &mut visited,
        emitted_module_identity_keys,
        loaded_module_overrides_by_path,
    };
    derive_static_scss_module_context_inner(
        request.style_path,
        request.style_source,
        request.variable_overrides,
        &mut context,
    )
}

fn derive_static_scss_module_context_inner(
    style_path: &str,
    style_source: &str,
    variable_overrides: &BTreeMap<String, String>,
    context: &mut StaticScssModuleDeriveContext<'_>,
) -> Option<StaticScssModuleContext> {
    let variable_overrides = omena_semantic::resolve_sass_module_effective_variable_overrides(
        style_path,
        variable_overrides,
        context.loaded_module_overrides_by_path,
    )?;
    let module_identity_key = omena_semantic::summarize_sass_module_instance_identity_key(
        style_path,
        &variable_overrides,
    );
    if !context.visited.insert(module_identity_key.clone()) {
        return Some(StaticScssModuleContext {
            module_output_css: String::new(),
            variable_exports: BTreeMap::new(),
            configurable_variable_names: BTreeSet::new(),
        });
    }

    let mut configurable_variable_names =
        derive_static_scss_stylesheet_module_configurable_variable_names(style_source);
    let style_source = scss_variable_overrides::apply_static_scss_module_variable_overrides(
        style_source,
        &variable_overrides,
    );
    let style_source = style_source.as_ref();

    let forward_evaluations = derive_static_scss_module_forward_evaluations_for_transform_context(
        style_path,
        style_source,
        &variable_overrides,
        context,
    )?;
    let mut variable_exports = derive_static_scss_stylesheet_module_variable_exports(style_source);
    for forward in &forward_evaluations {
        for (name, value) in &forward.variable_exports {
            variable_exports
                .entry(name.clone())
                .or_insert_with(|| value.clone());
        }
        configurable_variable_names.extend(forward.configurable_variable_names.iter().cloned());
    }

    let (evaluation_source, forward_mutation_count) = inline_static_scss_forward_rules(
        style_source,
        OmenaParserStyleDialect::Scss,
        &forward_evaluations,
        context.emitted_module_identity_keys,
    );
    let module_output_css = derive_static_stylesheet_module_evaluation(
        evaluation_source.as_str(),
        OmenaParserStyleDialect::Scss,
    )
    .and_then(|evaluation| {
        static_stylesheet_module_output_css_from_evaluation(evaluation_source.as_ref(), evaluation)
    })
    .unwrap_or_else(|| {
        if forward_mutation_count > 0 {
            evaluation_source
        } else {
            style_source.to_string()
        }
    });

    context.visited.remove(&module_identity_key);
    Some(StaticScssModuleContext {
        module_output_css,
        variable_exports,
        configurable_variable_names,
    })
}

fn derive_static_scss_module_forward_evaluations_for_transform_context(
    style_path: &str,
    style_source: &str,
    inherited_variable_overrides: &BTreeMap<String, String>,
    context: &mut StaticScssModuleDeriveContext<'_>,
) -> Option<Vec<StaticScssModuleForwardEvaluation>> {
    let facts =
        summarize_omena_query_omena_parser_style_facts(style_source, OmenaParserStyleDialect::Scss);

    let mut evaluations = Vec::new();
    for (forward_rule_ordinal, edge) in facts
        .sass_module_edges
        .iter()
        .filter(|edge| edge.kind == "sassForward")
        .enumerate()
    {
        let Some(resolved) = context.resolution_context.resolve_style_module_source(
            style_path,
            edge.source.as_str(),
            context.available_style_paths,
        ) else {
            continue;
        };
        let Some(source) = context.source_by_path.get(resolved.as_str()) else {
            continue;
        };
        let explicit_variable_overrides =
            omena_semantic::derive_sass_module_forward_variable_overrides_at_ordinal(
                style_source,
                forward_rule_ordinal,
            );
        let export_prefix = omena_semantic::derive_sass_forward_export_prefix_at_ordinal(
            style_source,
            forward_rule_ordinal,
        );
        let configurable_variable_names =
            derive_static_scss_module_configurable_variable_names_for_transform_context(
                resolved.as_str(),
                source,
                context.available_style_paths,
                context.source_by_path,
                context.resolution_context,
            );
        let variable_overrides = omena_semantic::derive_sass_forward_effective_variable_overrides(
            &explicit_variable_overrides,
            inherited_variable_overrides,
            export_prefix.as_deref(),
            edge.visibility_filter_kind,
            &edge.visibility_filter_names,
            &configurable_variable_names,
        );
        if !omena_semantic::sass_module_configuration_variables_are_valid(
            &variable_overrides,
            &configurable_variable_names,
        ) {
            continue;
        }
        let variable_overrides = omena_semantic::resolve_sass_module_effective_variable_overrides(
            resolved.as_str(),
            &variable_overrides,
            context.loaded_module_overrides_by_path,
        )?;
        let module_identity_key = omena_semantic::summarize_sass_module_instance_identity_key(
            resolved.as_str(),
            &variable_overrides,
        );
        let module_context = derive_static_scss_module_context_inner(
            resolved.as_str(),
            source,
            &variable_overrides,
            context,
        )?;
        evaluations.push(StaticScssModuleForwardEvaluation {
            source: edge.source.clone(),
            forward_rule_ordinal,
            module_identity_key,
            module_output_css: module_context.module_output_css,
            variable_exports: omena_semantic::filter_sass_forward_exports(
                omena_semantic::prefix_sass_forward_exports(
                    module_context.variable_exports,
                    export_prefix.as_deref(),
                ),
                edge.visibility_filter_kind,
                &edge.visibility_filter_names,
            ),
            configurable_variable_names:
                omena_semantic::filter_sass_forward_configurable_variable_names(
                    module_context.configurable_variable_names,
                    export_prefix.as_deref(),
                    edge.visibility_filter_kind,
                    &edge.visibility_filter_names,
                ),
        });
    }

    Some(evaluations)
}