fluidattacks-blends-domain 0.40.0

Blends functional core: pure AST graph to syntax graph (no_std)
Documentation
use alloc::string::String;
use alloc::vec::Vec;

use crate::stack::imports::{
    module_binding, script_module, symbol_binding, BindingReach, ImportSite,
};
use crate::stack::ImportBinding;
use crate::syntax::{SyntaxGraph, SyntaxKind, SyntaxNode};
use crate::{CodeGraph, NodeId};

pub fn reexport_sites(syntax: &SyntaxGraph) -> Vec<ImportSite> {
    syntax
        .nodes_by_type
        .get(&SyntaxKind::File)
        .into_iter()
        .flatten()
        .flat_map(|file| {
            syntax
                .ast_children(*file)
                .into_iter()
                .flat_map(move |statement| statement_sites(syntax, *file, statement))
        })
        .collect()
}

fn statement_sites(syntax: &SyntaxGraph, file: NodeId, statement: NodeId) -> Vec<ImportSite> {
    let Some(SyntaxNode::Export {
        expression: Some(specifier),
        declaration_id: None,
        alias,
        default_id: None,
    }) = syntax.nodes.get(&statement)
    else {
        return Vec::new();
    };
    let (module, relative_level) = script_module(specifier);
    let bindings = match alias.as_deref() {
        Some("*") => Vec::from([(
            statement,
            "*",
            symbol_binding(&module, relative_level, "*", "*"),
        )]),
        Some(namespace) => Vec::from([(
            statement,
            namespace,
            module_binding(&module, relative_level),
        )]),
        _ => named_bindings(syntax, statement, specifier, &module, relative_level),
    };
    bindings
        .into_iter()
        .map(|(node, symbol, binding)| ImportSite {
            node,
            scope: file,
            symbol: String::from(symbol),
            binding,
            reach: BindingReach::Importers,
        })
        .collect()
}

fn named_bindings<'graph>(
    syntax: &'graph SyntaxGraph,
    statement: NodeId,
    specifier: &str,
    module: &str,
    relative_level: u32,
) -> Vec<(NodeId, &'graph str, ImportBinding)> {
    syntax
        .ast_children(statement)
        .into_iter()
        .filter_map(|name| {
            let Some(SyntaxNode::Export {
                expression: Some(expression),
                alias: Some(exported),
                ..
            }) = syntax.nodes.get(&name)
            else {
                return None;
            };
            let imported = expression
                .strip_prefix(specifier)?
                .strip_prefix('.')
                .filter(|imported| !imported.is_empty())?;
            Some((
                name,
                exported.as_str(),
                symbol_binding(module, relative_level, imported, exported),
            ))
        })
        .collect()
}