use swc_core::ecma::{
ast::*,
visit::{Visit, VisitWith},
};
pub fn contains_cjs(m: &Module) -> bool {
let mut v = CjsFinder::default();
m.visit_with(&mut v);
v.found && !v.is_esm
}
#[derive(Copy, Clone, Default)]
struct CjsFinder {
found: bool,
is_esm: bool,
ignore_module: bool,
ignore_exports: bool,
}
impl CjsFinder {
fn adjust_state<'a, I>(&mut self, iter: I)
where
I: Iterator<Item = &'a Pat>,
{
iter.for_each(|p| {
if let Pat::Ident(i) = p {
if &*i.id.sym == "module" {
self.ignore_module = true;
}
if &*i.id.sym == "exports" {
self.ignore_exports = true;
}
}
})
}
}
impl Visit for CjsFinder {
fn visit_arrow_expr(&mut self, n: &ArrowExpr) {
let old_ignore_module = self.ignore_module;
let old_ignore_exports = self.ignore_exports;
self.adjust_state(n.params.iter());
n.visit_children_with(self);
self.ignore_module = old_ignore_module;
self.ignore_exports = old_ignore_exports;
}
fn visit_call_expr(&mut self, e: &CallExpr) {
if !self.ignore_exports
&& let Callee::Expr(expr) = &e.callee
&& let Expr::Member(member_expr) = &**expr
&& let (Expr::Ident(obj), MemberProp::Ident(prop)) = (&*member_expr.obj, &member_expr.prop)
&& &*obj.sym == "Object"
&& &*prop.sym == "defineProperty"
&& let Some(ExprOrSpread { expr: expr0, .. }) = e.args.first()
&& let Expr::Ident(arg0) = &**expr0
&& &*arg0.sym == "exports"
&& let Some(ExprOrSpread { expr: expr1, .. }) = e.args.get(1)
&& let Expr::Lit(Lit::Str(arg1)) = &**expr1
&& &*arg1.value == "__esModule"
{
self.found = true;
return;
}
e.callee.visit_with(self);
}
fn visit_class_method(&mut self, n: &ClassMethod) {
let old_ignore_module = self.ignore_module;
let old_ignore_exports = self.ignore_exports;
self.adjust_state(n.function.params.iter().map(|v| &v.pat));
n.visit_children_with(self);
self.ignore_module = old_ignore_module;
self.ignore_exports = old_ignore_exports;
}
fn visit_function(&mut self, n: &Function) {
let old_ignore_module = self.ignore_module;
let old_ignore_exports = self.ignore_exports;
self.adjust_state(n.params.iter().map(|v| &v.pat));
n.visit_children_with(self);
self.ignore_module = old_ignore_module;
self.ignore_exports = old_ignore_exports;
}
fn visit_member_expr(&mut self, e: &MemberExpr) {
if let Expr::Ident(obj) = &*e.obj
&& let MemberProp::Ident(prop) = &e.prop
{
if (!self.ignore_module && &*obj.sym == "module" && &*prop.sym == "exports")
|| (!self.ignore_exports && &*obj.sym == "exports")
{
self.found = true;
return;
}
}
e.obj.visit_with(self);
e.prop.visit_with(self);
}
fn visit_method_prop(&mut self, n: &MethodProp) {
let old_ignore_module = self.ignore_module;
let old_ignore_exports = self.ignore_exports;
self.adjust_state(n.function.params.iter().map(|v| &v.pat));
n.visit_children_with(self);
self.ignore_module = old_ignore_module;
self.ignore_exports = old_ignore_exports;
}
fn visit_module_decl(&mut self, n: &ModuleDecl) {
match n {
ModuleDecl::Import(_) => {}
_ => {
self.is_esm = true;
}
}
}
}