oxc_transformer 0.131.0

A collection of JavaScript tools written in Rust.
Documentation
use std::iter;

use oxc_allocator::{Box as ArenaBox, Vec as ArenaVec};
use oxc_ast::{NONE, ast::*};
use oxc_semantic::{ReferenceFlags, ScopeFlags, ScopeId, SymbolFlags};
use oxc_span::{GetSpan, SPAN};
use oxc_str::Ident;
use oxc_traverse::BoundIdentifier;

use crate::context::TraverseCtx;

/// `object` -> `object.call`.
pub fn create_member_callee<'a>(
    object: Expression<'a>,
    property: &'static str,
    span: Span,
    ctx: &TraverseCtx<'a>,
) -> Expression<'a> {
    let property = ctx.ast.identifier_name(SPAN, Str::from(property));
    Expression::from(ctx.ast.member_expression_static(span, object, property, false))
}

/// `object` -> `object.bind(this)`.
pub fn create_bind_call<'a>(
    callee: Expression<'a>,
    this: Expression<'a>,
    span: Span,
    ctx: &TraverseCtx<'a>,
) -> Expression<'a> {
    let callee = create_member_callee(callee, "bind", span, ctx);
    let arguments = ctx.ast.vec1(Argument::from(this));
    ctx.ast.expression_call(span, callee, NONE, arguments, false)
}

/// `object` -> `object.call(...arguments)`.
pub fn create_call_call<'a>(
    callee: Expression<'a>,
    this: Expression<'a>,
    span: Span,
    ctx: &TraverseCtx<'a>,
) -> Expression<'a> {
    let callee = create_member_callee(callee, "call", span, ctx);
    let arguments = ctx.ast.vec1(Argument::from(this));
    ctx.ast.expression_call(span, callee, NONE, arguments, false)
}

/// Wrap an `Expression` in an arrow function IIFE (immediately invoked function expression)
/// with a body block.
///
/// `expr` -> `(() => { return expr; })()`
pub fn wrap_expression_in_arrow_function_iife<'a>(
    expr: Expression<'a>,
    ctx: &mut TraverseCtx<'a>,
) -> Expression<'a> {
    let scope_id =
        ctx.insert_scope_below_expression(&expr, ScopeFlags::Arrow | ScopeFlags::Function);
    let span = expr.span();
    let stmts = ctx.ast.vec1(ctx.ast.statement_return(SPAN, Some(expr)));
    wrap_statements_in_arrow_function_iife(stmts, scope_id, span, ctx)
}

/// Wrap statements in an IIFE (immediately invoked function expression).
///
/// `x; y; z;` -> `(() => { x; y; z; })()`
pub fn wrap_statements_in_arrow_function_iife<'a>(
    stmts: ArenaVec<'a, Statement<'a>>,
    scope_id: ScopeId,
    span: Span,
    ctx: &TraverseCtx<'a>,
) -> Expression<'a> {
    let kind = FormalParameterKind::ArrowFormalParameters;
    let params = ctx.ast.alloc_formal_parameters(SPAN, kind, ctx.ast.vec(), NONE);
    let body = ctx.ast.alloc_function_body(SPAN, ctx.ast.vec(), stmts);
    let arrow = ctx.ast.expression_arrow_function_with_scope_id_and_pure_and_pife(
        SPAN, false, false, NONE, params, NONE, body, scope_id, false, false,
    );
    ctx.ast.expression_call(span, arrow, NONE, ctx.ast.vec(), false)
}

/// `object` -> `object.prototype`.
pub fn create_prototype_member<'a>(
    object: Expression<'a>,
    span: Span,
    ctx: &TraverseCtx<'a>,
) -> Expression<'a> {
    let property = ctx.ast.identifier_name(SPAN, Str::from("prototype"));
    let static_member = ctx.ast.member_expression_static(span, object, property, false);
    Expression::from(static_member)
}

/// `object` -> `object.a`.
pub fn create_property_access<'a>(
    span: Span,
    object: Expression<'a>,
    property: &str,
    ctx: &TraverseCtx<'a>,
) -> Expression<'a> {
    let property = ctx.ast.identifier_name(SPAN, ctx.ast.str(property));
    Expression::from(ctx.ast.member_expression_static(span, object, property, false))
}

/// `this.property`
#[inline]
pub fn create_this_property_access<'a>(
    span: Span,
    property: Ident<'a>,
    ctx: &TraverseCtx<'a>,
) -> MemberExpression<'a> {
    let object = ctx.ast.expression_this(span);
    let property = ctx.ast.identifier_name(SPAN, property);
    ctx.ast.member_expression_static(span, object, property, false)
}

/// `this.property`
#[inline]
pub fn create_this_property_assignment<'a>(
    span: Span,
    property: Ident<'a>,
    ctx: &TraverseCtx<'a>,
) -> AssignmentTarget<'a> {
    AssignmentTarget::from(create_this_property_access(span, property, ctx))
}

/// Create assignment to a binding.
pub fn create_assignment<'a>(
    binding: &BoundIdentifier<'a>,
    value: Expression<'a>,
    span: Span,
    ctx: &mut TraverseCtx<'a>,
) -> Expression<'a> {
    ctx.ast.expression_assignment(
        span,
        AssignmentOperator::Assign,
        binding.create_target(ReferenceFlags::Write, ctx),
        value,
    )
}

/// `super(...args);`
pub fn create_super_call<'a>(
    args_binding: &BoundIdentifier<'a>,
    ctx: &mut TraverseCtx<'a>,
) -> Expression<'a> {
    ctx.ast.expression_call(
        SPAN,
        ctx.ast.expression_super(SPAN),
        NONE,
        ctx.ast
            .vec1(ctx.ast.argument_spread_element(SPAN, args_binding.create_read_expression(ctx))),
        false,
    )
}

/// * With super class:
///   `constructor(..._args) { super(..._args); statements }`
/// * Without super class:
//    `constructor() { statements }`
pub fn create_class_constructor<'a, 'c>(
    stmts_iter: impl IntoIterator<Item = Statement<'a>> + 'c,
    has_super_class: bool,
    scope_id: ScopeId,
    ctx: &mut TraverseCtx<'a>,
) -> ClassElement<'a> {
    // Add `super(..._args);` statement and `..._args` param if class has a super class.
    // `constructor(..._args) { super(..._args); /* prop initialization */ }`
    // TODO(improve-on-babel): We can use `arguments` instead of creating `_args`.
    let mut params_rest = None;
    let stmts = if has_super_class {
        let args_binding = ctx.generate_uid("args", scope_id, SymbolFlags::FunctionScopedVariable);
        let rest_element =
            ctx.ast.binding_rest_element(SPAN, args_binding.create_binding_pattern(ctx));
        params_rest =
            Some(ctx.ast.alloc_formal_parameter_rest(SPAN, ctx.ast.vec(), rest_element, NONE));
        ctx.ast.vec_from_iter(
            iter::once(ctx.ast.statement_expression(SPAN, create_super_call(&args_binding, ctx)))
                .chain(stmts_iter),
        )
    } else {
        ctx.ast.vec_from_iter(stmts_iter)
    };

    let params = ctx.ast.alloc_formal_parameters(
        SPAN,
        FormalParameterKind::FormalParameter,
        ctx.ast.vec(),
        params_rest,
    );

    create_class_constructor_with_params(stmts, params, scope_id, ctx)
}

//  `constructor(params) { statements }`
pub fn create_class_constructor_with_params<'a>(
    stmts: ArenaVec<'a, Statement<'a>>,
    params: ArenaBox<'a, FormalParameters<'a>>,
    scope_id: ScopeId,
    ctx: &TraverseCtx<'a>,
) -> ClassElement<'a> {
    create_class_method(
        ctx.ast.vec(),
        PropertyKey::StaticIdentifier(
            ctx.ast.alloc_identifier_name(SPAN, Str::from("constructor")),
        ),
        MethodDefinitionKind::Constructor,
        params,
        None,
        stmts,
        false,
        false,
        scope_id,
        ctx,
    )
}

/// Create a `MethodDefinition` class element wrapping a function expression.
pub fn create_class_method<'a>(
    decorators: ArenaVec<'a, Decorator<'a>>,
    key: PropertyKey<'a>,
    kind: MethodDefinitionKind,
    params: ArenaBox<'a, FormalParameters<'a>>,
    return_type: Option<ArenaBox<'a, TSTypeAnnotation<'a>>>,
    stmts: ArenaVec<'a, Statement<'a>>,
    computed: bool,
    is_static: bool,
    scope_id: ScopeId,
    ctx: &TraverseCtx<'a>,
) -> ClassElement<'a> {
    ClassElement::MethodDefinition(ctx.ast.alloc_method_definition(
        SPAN,
        MethodDefinitionType::MethodDefinition,
        decorators,
        key,
        ctx.ast.alloc_function_with_scope_id(
            SPAN,
            FunctionType::FunctionExpression,
            None,
            false,
            false,
            false,
            NONE,
            NONE,
            params,
            return_type,
            Some(ctx.ast.alloc_function_body(SPAN, ctx.ast.vec(), stmts)),
            scope_id,
        ),
        kind,
        computed,
        is_static,
        false,
        false,
        None,
    ))
}