use oxc_ast::ast::*;
use oxc_span::SPAN;
use oxc_traverse::{Ancestor, Traverse};
use crate::{context::TraverseCtx, state::TransformState};
const SELF: &str = "__self";
pub struct JsxSelf;
impl JsxSelf {
pub fn new() -> Self {
Self
}
}
impl<'a> Traverse<'a, TransformState<'a>> for JsxSelf {
fn enter_jsx_opening_element(
&mut self,
elem: &mut JSXOpeningElement<'a>,
ctx: &mut TraverseCtx<'a>,
) {
self.add_self_this_attribute(elem, ctx);
}
}
impl<'a> JsxSelf {
fn is_inside_constructor(ctx: &TraverseCtx<'a>) -> bool {
for scope_id in ctx.ancestor_scopes() {
let flags = ctx.scoping().scope_flags(scope_id);
if flags.is_block() || flags.is_arrow() {
continue;
}
return flags.is_constructor();
}
unreachable!(); }
fn has_no_super_class(ctx: &TraverseCtx<'a>) -> bool {
for ancestor in ctx.ancestors() {
if let Ancestor::ClassBody(class) = ancestor {
return class.super_class().is_none();
}
}
true
}
pub fn get_object_property_kind_for_jsx_plugin(
ctx: &TraverseCtx<'a>,
) -> ObjectPropertyKind<'a> {
let kind = PropertyKind::Init;
let key = ctx.ast.property_key_static_identifier(SPAN, SELF);
let value = ctx.ast.expression_this(SPAN);
ctx.ast.object_property_kind_object_property(SPAN, kind, key, value, false, false, false)
}
pub fn can_add_self_attribute(ctx: &TraverseCtx<'a>) -> bool {
!Self::is_inside_constructor(ctx) || Self::has_no_super_class(ctx)
}
#[expect(clippy::unused_self, clippy::needless_pass_by_ref_mut)]
fn add_self_this_attribute(&self, elem: &mut JSXOpeningElement<'a>, ctx: &mut TraverseCtx<'a>) {
if elem.attributes.iter().any(|item| {
matches!(item, JSXAttributeItem::Attribute(attribute)
if matches!(&attribute.name, JSXAttributeName::Identifier(ident) if ident.name == SELF))
}) {
return;
}
let name = ctx.ast.jsx_attribute_name_identifier(SPAN, SELF);
let value = {
let jsx_expr = JSXExpression::from(ctx.ast.expression_this(SPAN));
ctx.ast.jsx_attribute_value_expression_container(SPAN, jsx_expr)
};
let attribute = ctx.ast.jsx_attribute_item_attribute(SPAN, name, Some(value));
elem.attributes.push(attribute);
}
}