#[allow(clippy::wildcard_imports, reason = "many call AST types used")]
use oxc_ast::ast::*;
use fallow_types::extract::ImportedName;
use super::super::ModuleInfoExtractor;
const ENTITY_ARGUMENT_METHODS: &[&str] = &[
"getRepository",
"getCustomRepository",
"getTreeRepository",
"getMongoRepository",
"createQueryBuilder",
];
const ORM_FIND_METHODS: &[&str] = &[
"find",
"findBy",
"findOne",
"findOneBy",
"findOneOrFail",
"findOneByOrFail",
"findAndCount",
"findAndCountBy",
];
const ENTITY_TYPE_ARGUMENT_METHODS: &[&str] = &[
"getRepository",
"getCustomRepository",
"getTreeRepository",
"getMongoRepository",
];
const ORM_PACKAGES: &[&str] = &["typeorm", "@nestjs/typeorm"];
const ORM_PACKAGE_SCOPE: &str = "@mikro-orm/";
const REGISTER_ENUM_TYPE_SOURCES: &[&str] = &["@nestjs/graphql", "type-graphql"];
const REGISTER_ENUM_TYPE: &str = "registerEnumType";
impl ModuleInfoExtractor {
pub(super) fn record_reflective_whole_object_use(&mut self, expr: &CallExpression<'_>) {
if let Some(name) = self.reflective_call_target(expr) {
self.record_whole_object_identifier_use(name.as_str());
}
}
fn reflective_call_target(&self, expr: &CallExpression<'_>) -> Option<String> {
match &expr.callee {
Expression::StaticMemberExpression(member) => self.orm_entity_target(expr, member),
Expression::Identifier(callee) if self.is_register_enum_type(callee.name.as_str()) => {
first_identifier_argument(expr)
}
_ => None,
}
}
fn is_register_enum_type(&self, local: &str) -> bool {
!self.nested_scope_shadows(local)
&& self.imports.iter().any(|import| {
import.local_name == local
&& !import.is_type_only
&& REGISTER_ENUM_TYPE_SOURCES.contains(&import.source.as_str())
&& matches!(
&import.imported_name,
ImportedName::Named(name) if name == REGISTER_ENUM_TYPE
)
})
}
fn orm_entity_target(
&self,
expr: &CallExpression<'_>,
member: &StaticMemberExpression<'_>,
) -> Option<String> {
let method = member.property.name.as_str();
if ENTITY_TYPE_ARGUMENT_METHODS.contains(&method)
&& let Some(name) = first_type_argument_name(expr)
{
return Some(name);
}
let entity_method = ENTITY_ARGUMENT_METHODS.contains(&method)
|| (ORM_FIND_METHODS.contains(&method) && self.imports_orm_package());
if entity_method {
return first_identifier_argument(expr);
}
None
}
fn imports_orm_package(&self) -> bool {
self.imports.iter().any(|import| {
let source = import.source.as_str();
ORM_PACKAGES.contains(&source) || source.starts_with(ORM_PACKAGE_SCOPE)
})
}
}
fn first_identifier_argument(expr: &CallExpression<'_>) -> Option<String> {
match expr.arguments.first()? {
Argument::Identifier(ident) => Some(ident.name.to_string()),
_ => None,
}
}
fn first_type_argument_name(expr: &CallExpression<'_>) -> Option<String> {
let first = expr.type_arguments.as_deref()?.params.first()?;
let TSType::TSTypeReference(reference) = first else {
return None;
};
match &reference.type_name {
TSTypeName::IdentifierReference(ident) => Some(ident.name.to_string()),
_ => None,
}
}