reluxscript

Crates.ioreluxscript
lib.rsreluxscript
version0.1.4
created_at2025-11-25 07:17:22.260875+00
updated_at2025-12-04 12:45:17.232668+00
descriptionWrite AST transformations once. Compile to Babel, SWC, and beyond.
homepagehttps://reluxscript.com
repositoryhttps://github.com/reluxscript/reluxscript
max_upload_size
id1949279
size3,718,724
(sjseto0519)

documentation

https://docs.reluxscript.com

README

ReluxScript

A language that compiles to both Babel (JavaScript) and SWC (Rust) plugins from a single source.

Overview

ReluxScript is a domain-specific language for writing AST transformation plugins. Write your plugin once in ReluxScript, and compile it to:

  • Babel plugin (JavaScript) - for Node.js/npm ecosystem
  • SWC plugin (Rust) - for high-performance native builds

Installation

cd reluxscript
cargo build --release

Usage

Check a file for errors

cargo run -- check examples/my_plugin.rsc

Build a plugin

# Build for both targets (default)
cargo run -- build examples/my_plugin.rsc -o dist

# Build for Babel only
cargo run -- build examples/my_plugin.rsc -o dist -t babel

# Build for SWC only
cargo run -- build examples/my_plugin.rsc -o dist -t swc

Debug commands

# Tokenize (view lexer output)
cargo run -- lex examples/my_plugin.rsc

# Parse (view AST)
cargo run -- parse examples/my_plugin.rsc

Language Syntax

ReluxScript uses a Rust-like syntax optimized for AST transformations.

Basic Plugin Structure

/// My transformation plugin
plugin MyPlugin {
    /// Visit function declarations
    pub fn visit_function_declaration(node: &FunctionDeclaration) {
        let name = node.id.name.clone();

        if name.starts_with("use") {
            // Transform hooks...
        }
    }
}

Structs

struct Component {
    name: Str,
    props: Vec<Prop>,
    hooks: Vec<Str>,
}

struct Prop {
    name: Str,
    prop_type: Str,
}

Pattern Matching with matches!

pub fn visit_call_expression(node: &CallExpression) {
    if matches!(node.callee, Identifier) {
        let name = node.callee.name.clone();
        // ...
    }
}

Traverse Blocks

Traverse into child nodes with nested visitors:

pub fn visit_function_declaration(node: &FunctionDeclaration) {
    let mut hooks: Vec<Str> = vec![];

    traverse(node.body) {
        fn visit_call_expression(call: &CallExpression) {
            if matches!(call.callee, Identifier) {
                if call.callee.name.starts_with("use") {
                    hooks.push(call.callee.name.clone());
                }
            }
        }
    }
}

Collections

// Vec
let mut items: Vec<Str> = vec![];
items.push("hello");
let joined = items.join(", ");

// HashMap
let mut map: HashMap<Str, Str> = HashMap::new();
map.insert("key", "value");
if map.contains_key(&"key") {
    let value = map.get(&"key");
}

// HashSet
let mut set: HashSet<Str> = HashSet::new();
set.insert("item");
if set.contains(&"item") {
    // ...
}

String Formatting

let name = "World";
let greeting = format!("Hello, {}!", name);

// Multi-line
let code = format!(
    "public class {} {{\n    // body\n}}",
    class_name
);

// Concatenation
let path = base + "/" + &filename;

// String builder
let mut s = String::new();
s.push_str("line 1\n");
s.push_str("line 2\n");

File I/O

use fs;

fn save_output(content: &Str, path: &Str) {
    fs::create_dir_all(&"output").unwrap();
    fs::write(path, content).unwrap();
}

fn load_config(path: &Str) -> Str {
    return fs::read_to_string(path).unwrap();
}

JSON Serialization

use json;

#[derive(Serialize)]
struct Config {
    name: Str,
    version: Str,
}

fn save_config(config: &Config) {
    let json_str = json::to_string_pretty(config).unwrap();
    fs::write(&"config.json", &json_str).unwrap();
}

AST Node Types

ReluxScript includes mappings for all Babel/SWC AST nodes. Common ones:

Expressions

  • Identifier
  • CallExpression
  • MemberExpression
  • ArrayExpression
  • ObjectExpression
  • ArrowFunctionExpression
  • JSXElement

Statements

  • FunctionDeclaration
  • VariableDeclaration
  • VariableDeclarator
  • BlockStatement
  • ReturnStatement
  • IfStatement

TypeScript

  • TSInterfaceDeclaration
  • TSTypeAnnotation
  • TSPropertySignature

Patterns

  • ArrayPattern
  • ObjectPattern

Generated Output

Babel Output

module.exports = function({ types: t }) {
  return {
    visitor: {
      FunctionDeclaration(path) {
        const node = path.node;
        // Your transformed code...
      }
    }
  };
};

SWC Output

use swc_core::ecma::{
    ast::*,
    visit::{VisitMut, VisitMutWith},
};

pub struct MyPlugin;

impl VisitMut for MyPlugin {
    fn visit_mut_fn_decl(&mut self, node: &mut FnDecl) {
        // Your transformed code...
        node.visit_mut_children_with(self);
    }
}

Project Structure

reluxscript/
├── src/
│   ├── lib.rs           # Library exports
│   ├── main.rs          # CLI entry point
│   ├── lexer/           # Tokenizer
│   ├── parser/          # Parser and AST definitions
│   ├── semantic/        # Type checking, resolution, lowering
│   ├── codegen/         # Babel and SWC code generators
│   └── mapping/         # AST node/field mappings
├── examples/            # Example ReluxScript plugins
├── tests/
│   └── enhancements/    # Feature test cases
└── Cargo.toml

Examples

See the examples/ directory for sample plugins:

  • build_member_path.rsc - Helper function for building member expressions
  • tsx_parser_test/ - Extract interfaces and hooks from TSX

Documentation

License

MIT

Commit count: 0

cargo fmt