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()
}