use crate::models::ImportRelation;
use super::super::context::{ExternalImportBinding, ExtractedImports, ImportResolutionContext};
use super::super::helpers::{
collapse_whitespace, extract_js_import_clause, extract_js_module_specifier, split_alias,
split_top_level,
};
use super::super::predicates::{is_external_js_module, is_external_python_module};
use super::push_unparsed_import;
pub(crate) fn parse_python_import_statement(
text: &str,
rel_path: &str,
import_context: &ImportResolutionContext,
extracted: &mut ExtractedImports,
) -> anyhow::Result<()> {
if let Some(rest) = text.strip_prefix("import ") {
let Ok(entries) = split_top_level(rest, ',') else {
return Ok(());
};
for entry in entries {
let entry = entry.trim();
if entry.is_empty() {
continue;
}
let (module, alias) = split_alias(entry);
if module.is_empty() || module.chars().any(char::is_whitespace) {
continue;
}
extracted.imports.push(ImportRelation {
file_path: rel_path.to_string(),
module_name: module.to_string(),
});
if is_external_python_module(module, import_context) {
let local_alias = alias
.map(ToOwned::to_owned)
.unwrap_or_else(|| module.split('.').next().unwrap_or(module).to_string());
extracted
.bindings
.member
.insert(local_alias, module.to_string());
}
}
return Ok(());
}
let Some(rest) = text.strip_prefix("from ") else {
push_unparsed_import(rel_path, text, extracted)?;
return Ok(());
};
let Some((module, imported)) = rest.split_once(" import ") else {
push_unparsed_import(rel_path, text, extracted)?;
return Ok(());
};
let module = module.trim();
if module.is_empty() {
return Ok(());
}
extracted.imports.push(ImportRelation {
file_path: rel_path.to_string(),
module_name: module.to_string(),
});
if !is_external_python_module(module, import_context) {
return Ok(());
}
let imported = imported.trim().trim_matches(|ch| matches!(ch, '(' | ')'));
let Ok(entries) = split_top_level(imported, ',') else {
return Ok(());
};
for entry in entries {
let entry = entry.trim();
if entry.is_empty() || entry == "*" {
continue;
}
let (imported_name, alias) = split_alias(entry);
let local_alias = alias.unwrap_or(imported_name).to_string();
extracted.bindings.bare.insert(
local_alias.clone(),
ExternalImportBinding {
module: module.to_string(),
callee_name: imported_name.to_string(),
},
);
extracted
.bindings
.member
.insert(local_alias, module.to_string());
}
Ok(())
}
pub(crate) fn parse_js_import_statement(
text: &str,
rel_path: &str,
import_context: &ImportResolutionContext,
extracted: &mut ExtractedImports,
) -> anyhow::Result<()> {
let normalized = collapse_whitespace(text);
let Some(specifier) = extract_js_module_specifier(&normalized) else {
push_unparsed_import(rel_path, &normalized, extracted)?;
return Ok(());
};
extracted.imports.push(ImportRelation {
file_path: rel_path.to_string(),
module_name: specifier.clone(),
});
if !is_external_js_module(&specifier, import_context) {
return Ok(());
}
let Some(clause) = extract_js_import_clause(&normalized) else {
return Ok(());
};
let clause = clause.trim();
if clause.is_empty() || clause.starts_with("type ") {
return Ok(());
}
let Ok(parts) = split_top_level(clause, ',') else {
return Ok(());
};
for part in parts {
let part = part.trim();
if part.is_empty() {
continue;
}
if let Some(alias) = part.strip_prefix("* as ") {
let alias = alias.trim();
if !alias.is_empty() {
extracted
.bindings
.member
.insert(alias.to_string(), specifier.clone());
}
continue;
}
if part.starts_with('{') && part.ends_with('}') {
let inner = &part[1..part.len() - 1];
let Ok(items) = split_top_level(inner, ',') else {
return Ok(());
};
for item in items {
let item = item.trim();
if item.is_empty() || item.starts_with("type ") {
continue;
}
let (imported_name, alias) = split_alias(item);
let local_alias = alias.unwrap_or(imported_name).to_string();
extracted.bindings.bare.insert(
local_alias.clone(),
ExternalImportBinding {
module: specifier.clone(),
callee_name: imported_name.to_string(),
},
);
extracted
.bindings
.member
.insert(local_alias, specifier.clone());
}
continue;
}
if part.starts_with("type ") {
continue;
}
let alias = part.trim();
if alias.is_empty() {
continue;
}
extracted.bindings.bare.insert(
alias.to_string(),
ExternalImportBinding {
module: specifier.clone(),
callee_name: "default".to_string(),
},
);
extracted
.bindings
.member
.insert(alias.to_string(), specifier.clone());
}
Ok(())
}