reluxscript 0.1.4

Write AST transformations once. Compile to Babel, SWC, and beyond.
Documentation
//! SWC (Rust) code generator for ReluxScript
//!
//! This module generates Rust code for SWC plugins/writers from ReluxScript AST.
//! Refactored into focused submodules for maintainability.

use crate::parser::*;
use crate::mapping::{get_node_mapping, get_node_mapping_by_visitor};
use super::swc_decorator::{DecoratedProgram, DecoratedTopLevelDecl, DecoratedPlugin, DecoratedWriter, DecoratedPluginItem, DecoratedFnDecl};
use super::decorated_ast::{DecoratedPattern, DecoratedPatternKind, DecoratedExpr, DecoratedExprKind, DecoratedStmt, DecoratedIfStmt, DecoratedBlock};

// Submodules
mod emit;
mod detection;
mod type_mapping;
mod type_inference;
mod patterns;
mod expressions;
mod statements;
mod structures;
mod visitors;
mod top_level;

/// Generator for SWC plugin Rust code
pub struct SwcGenerator {
    output: String,
    indent: usize,
    /// Maps ReluxScript parameter names to SWC names (e.g., "node" -> "n")
    param_renames: std::collections::HashMap<String, String>,
    /// Plugin name for generating nested visitors
    plugin_name: String,
    /// Hoisted inline visitor structs
    hoisted_visitors: Vec<String>,
    /// Variables captured from outer scope (need self. prefix)
    captured_vars: std::collections::HashSet<String>,
    /// Whether json serialization is needed (adds Serialize, Deserialize derives)
    uses_json: bool,
    /// Whether parser module is used (needs helper functions)
    uses_parser: bool,
    /// Whether codegen module is used (needs swc_ecma_codegen imports)
    uses_codegen: bool,
    /// Associated functions (no self parameter) - need Self:: prefix when called
    associated_functions: std::collections::HashSet<String>,
    /// Whether we're generating a writer (uses Visit) vs plugin (uses VisitMut)
    is_writer: bool,
    /// DEPRECATED: Type environment for inference (will be removed)
    #[allow(dead_code)]
    type_env: crate::codegen::type_context::TypeEnvironment,
}

impl SwcGenerator {
    pub fn new() -> Self {
        Self {
            output: String::new(),
            indent: 0,
            param_renames: std::collections::HashMap::new(),
            plugin_name: String::new(),
            hoisted_visitors: Vec::new(),
            captured_vars: std::collections::HashSet::new(),
            uses_json: false,
            uses_parser: false,
            uses_codegen: false,
            associated_functions: std::collections::HashSet::new(),
            is_writer: false,
            type_env: crate::codegen::type_context::TypeEnvironment::new(),
        }
    }

    /// Generate Rust code for an SWC plugin (original AST)
    pub fn generate(&mut self, program: &Program) -> String {
        // Check what std types are used
        let (uses_hashmap, uses_hashset) = self.detect_std_collections(program);

        // Check if json, parser, or codegen modules are used
        for use_stmt in &program.uses {
            if use_stmt.path == "json" {
                self.uses_json = true;
            }
            if use_stmt.path == "parser" {
                self.uses_parser = true;
            }
            if use_stmt.path == "codegen" {
                self.uses_codegen = true;
            }
        }

        // Emit file header
        self.emit_line("// Generated by ReluxScript compiler");
        self.emit_line("// Do not edit manually");
        self.emit_line("");
        self.emit_line("use swc_common::{Span, DUMMY_SP, SyntaxContext};");
        self.emit_line("use swc_ecma_ast::*;");
        self.emit_line("use swc_ecma_visit::{VisitMut, VisitMutWith};");

        // Add std::collections imports if needed
        if uses_hashmap && uses_hashset {
            self.emit_line("use std::collections::{HashMap, HashSet};");
        } else if uses_hashmap {
            self.emit_line("use std::collections::HashMap;");
        } else if uses_hashset {
            self.emit_line("use std::collections::HashSet;");
        }

        // Process use statements
        for use_stmt in &program.uses {
            if !use_stmt.path.starts_with("./") && !use_stmt.path.starts_with("../") {
                match use_stmt.path.as_str() {
                    "fs" => {
                        self.emit_line("use std::fs;");
                        self.emit_line("use std::path::Path;");
                    }
                    "json" => {
                        self.emit_line("use serde::{Serialize, Deserialize};");
                        self.emit_line("use serde_json;");
                    }
                    "io" => {
                        self.emit_line("use std::io;");
                        self.emit_line("use std::io::{Read, Write};");
                    }
                    "parser" => {
                        self.emit_line("use swc_ecma_parser::{Parser, Syntax, TsConfig, StringInput};");
                    }
                    "codegen" => {
                        self.emit_line("use swc_ecma_codegen::{Emitter, text_writer::JsWriter};");
                    }
                    _ => {}
                }
            }
        }

        self.emit_line("");

        // Generate the main declaration (plugin, writer, or module)
        match &program.decl {
            TopLevelDecl::Plugin(plugin) => {
                self.gen_plugin(plugin);
            }
            TopLevelDecl::Writer(writer) => {
                self.gen_writer(writer);
            }
            TopLevelDecl::Module(module) => {
                self.gen_module(module);
            }
        }

        std::mem::take(&mut self.output)
    }

    /// Generate Rust code for an SWC plugin (decorated AST)
    pub fn generate_decorated(&mut self, program: &DecoratedProgram) -> String {
        self.emit_line("// Generated by ReluxScript compiler (decorated AST)");
        self.emit_line("// Do not edit manually");
        self.emit_line("");
        self.emit_line("use swc_common::{Span, DUMMY_SP, SyntaxContext};");
        self.emit_line("use swc_ecma_ast::*;");
        self.emit_line("use swc_ecma_visit::{VisitMut, VisitMutWith};");
        self.emit_line("");

        match &program.decl {
            DecoratedTopLevelDecl::Plugin(plugin) => self.gen_decorated_plugin(plugin),
            DecoratedTopLevelDecl::Writer(writer) => self.gen_decorated_writer(writer),
            DecoratedTopLevelDecl::Undecorated(decl) => {
                eprintln!("[WARNING] Falling back to undecorated codegen");
                self.emit_line("// TODO: Undecorated top-level declaration");
            }
        }

        std::mem::take(&mut self.output)
    }
}