This guide explains how minimact_full.rsc integrates with the converted helper modules from rustscript-plugin-minimact/.
minimact_full.rsc (Main Transpiler)
↓
Uses helper modules:
├── utils/helpers.rsc (String escaping, component detection)
├── utils/hex_path.rsc (Element path generation)
├── utils/style_converter.rsc (CSS conversion)
├── types/type_conversion.rsc (TypeScript → C# types)
├── analyzers/*.rsc (Pattern detection, classification)
├── extractors/*.rsc (Template extraction, binding analysis)
└── generators/*.rsc (C# code generation)
Utils:
- ✅
helpers.rsc- String escaping, component name detection - ✅
hex_path.rsc- Hex path generation for stable element IDs - ✅
style_converter.rsc- camelCase → kebab-case conversion
Types:
- ✅
type_conversion.rsc- TypeScript → C# type mapping
Analyzers:
- ✅
classification.rsc- Node classification (static/client/server/hybrid) - ✅
detection.rsc- Pattern detection (spread props, dynamic children) - ✅
hook_detector.rsc- Custom hook detection
Extractors:
- ✅
is_simple_expression.rsc- Expression classification - ✅
build_member_path.rsc- Member expression path building
Generators:
- ✅
string_methods.rsc- JavaScript → C# method conversion
Integration:
- ✅
test_minimact_full.rsc- Integration test
Current code in minimact_full.rsc:334:
fn expr_to_csharp(expr: &Expr) -> Str {
if matches!(expr, StringLiteral) {
return format!("\"{}\"", expr.value); // ❌ No escaping!
}
...
}
Should be:
use "../rustscript-plugin-minimact/utils/helpers.rsc" { escape_csharp_string };
fn expr_to_csharp(expr: &Expr) -> Str {
if matches!(expr, StringLiteral) {
// ✅ Properly escaped for C#
return format!("\"{}\"", escape_csharp_string(&expr.value));
}
...
}
Why: The escape_csharp_string() function properly escapes:
\→\\"→\"\n→\\n\r→\\r\t→\\t
Current code in minimact_full.rsc:199-206:
fn is_pascal_case(name: &Str) -> bool {
if name.len() == 0 {
return false;
}
let first_char = name.chars().next().unwrap();
return first_char.is_uppercase();
}
Should be:
use "../rustscript-plugin-minimact/utils/helpers.rsc" { is_component_name };
// Replace is_pascal_case() calls with is_component_name()
Why: DRY principle - reuse tested logic instead of duplicating.
Current code in minimact_full.rsc:352-373:
fn infer_csharp_type(expr: &Expr) -> Str {
if matches!(expr, StringLiteral) {
return "string".to_string();
} else if matches!(expr, NumericLiteral) {
let val = expr.value;
if val == val.floor() {
return "int".to_string();
} else {
return "double".to_string();
}
}
...
}
Should be:
use "../rustscript-plugin-minimact/types/type_conversion.rsc" { infer_type };
// Replace infer_csharp_type() with direct call to infer_type()
Why: The type_conversion.rsc module:
- Handles all literal types (string, number, boolean, null)
- Differentiates integers vs floats correctly
- Supports array and object inference
- Handles @minimact/mvc special types (decimal, Guid, DateTime, etc.)
Current state: Not yet integrated
Recommendation:
use "../rustscript-plugin-minimact/utils/hex_path.rsc" { HexPathGenerator };
writer MinimactTranspiler {
struct State {
// Add hex path generator to state
hex_path_gen: HexPathGenerator,
...
}
fn init() -> State {
State {
hex_path_gen: HexPathGenerator::default(),
...
}
}
// Use when generating VNode tree
fn generate_vnode_tree(...) {
let element_path = self.hex_path_gen.next(&parent_path);
// Assign to element for stable identity
}
}
Why: Minimact's architecture depends on stable hex paths for:
- Predictive patch targeting
- Hot reload state preservation
- Precise DOM updates
Recommendation:
use "../rustscript-plugin-minimact/utils/style_converter.rsc" {
camel_to_kebab,
convert_style_object_to_css
};
// When encountering JSX style attributes:
fn visit_jsx_attribute(attr: &JSXAttribute) {
if attr.name == "style" {
if let Some(obj) = attr.value.as_object() {
let css = convert_style_object_to_css(obj)?;
// Emit CSS string to C# code
}
}
}
Why: Minimact converts inline React styles to CSS strings for server-side rendering.
Recommendation:
use "../rustscript-plugin-minimact/analyzers/detection.rsc" {
has_spread_props,
has_dynamic_children
};
use "../rustscript-plugin-minimact/analyzers/classification.rsc" {
classify_node,
Dependency
};
fn analyze_jsx_element(elem: &JSXElement) {
// Check for dynamic patterns
if has_spread_props(&elem.opening_element.attributes) {
// Mark component as having dynamic props
}
if has_dynamic_children(&elem.children) {
// Mark component as having dynamic children
}
// Classify based on dependencies
let deps = collect_dependencies(elem);
let classification = classify_node(&deps);
// classification: "static", "client", "server", or "hybrid"
}
Why: Proper classification enables:
- Optimal code generation (static vs dynamic)
- Prediction system optimization
- Client vs server rendering decisions
- Replace
is_pascal_casewithis_component_namefromhelpers.rsc - Replace
expr_to_csharpstring handling withescape_csharp_string - Replace
infer_csharp_typewithinfer_typefromtype_conversion.rsc
- Integrate
HexPathGeneratorinto writer state - Generate hex paths for all JSX elements
- Emit hex paths in generated C# VNode tree
- Use
convert_style_object_to_cssfor style attributes - Generate CSS strings in C# code
- Use
has_spread_propsandhas_dynamic_childrenfor analysis - Use
classify_nodefor server/client classification - Generate optimized code based on classification
- Integrate template extraction from
extractors/modules - Use
extract_ternary_element_templatefor conditionals - Use
extract_logical_and_element_templatefor logical expressions
- Use
generators/modules for C# code generation - Generate method calls using
handle_string_methods - Generate proper C# expressions for all JavaScript patterns
/// Minimact TSX to C# Transpiler (Integrated Version)
use fs;
use json;
// Import helper modules
use "../rustscript-plugin-minimact/utils/helpers.rsc" {
escape_csharp_string,
is_component_name
};
use "../rustscript-plugin-minimact/utils/hex_path.rsc" { HexPathGenerator };
use "../rustscript-plugin-minimact/types/type_conversion.rsc" {
infer_type,
ts_type_to_csharp_type
};
use "../rustscript-plugin-minimact/analyzers/classification.rsc" {
classify_node,
Dependency
};
use "../rustscript-plugin-minimact/analyzers/detection.rsc" {
has_spread_props,
has_dynamic_children
};
writer MinimactTranspiler {
struct State {
csharp: CodeBuilder,
templates: HashMap<Str, Template>,
hooks: Vec<HookSignature>,
hex_path_gen: HexPathGenerator, // ✨ Added
current_component: Option<ComponentInfo>,
components: Vec<ComponentInfo>,
}
fn init() -> State {
State {
csharp: CodeBuilder::new(),
templates: HashMap::new(),
hooks: vec![],
hex_path_gen: HexPathGenerator::default(), // ✨ Added
current_component: None,
components: vec![],
}
}
pub fn visit_function_declaration(node: &FunctionDeclaration) {
let name = node.id.name.clone();
// ✅ Use helper instead of local function
if !is_component_name(&name) {
return;
}
let mut component = ComponentInfo::new(name.clone());
if node.params.len() > 0 {
extract_props(&node.params[0], &mut component);
}
if let Some(body) = &node.body {
traverse(body) capturing [&mut component] {
fn visit_variable_declarator(decl: &VariableDeclarator) {
if let Some(init) = &decl.init {
if matches!(init, CallExpression) {
extract_hook_from_call(init, &decl.id, &mut component);
}
}
}
fn visit_return_statement(ret: &ReturnStatement) {
if let Some(arg) = &ret.argument {
if matches!(arg, JSXElement) {
component.render_body = Some(arg.clone());
}
}
}
}
}
self.components.push(component);
}
fn finish(&self) -> TranspilerOutput {
let mut csharp_code = String::new();
for component in &self.components {
let code = generate_csharp_class(&component);
csharp_code.push_str(&code);
csharp_code.push_str("\n");
}
let mut all_templates: HashMap<Str, Template> = HashMap::new();
for component in &self.components {
for (key, template) in &component.templates {
let full_key = format!("{}.{}", component.name, key);
all_templates.insert(full_key, template.clone());
}
}
TranspilerOutput {
csharp: csharp_code,
templates: json::to_string_pretty(&all_templates).unwrap(),
hooks: json::to_string_pretty(&self.hooks).unwrap(),
}
}
}
// Helper Functions (Updated to use modules)
fn expr_to_csharp(expr: &Expr) -> Str {
if matches!(expr, StringLiteral) {
// ✅ Properly escape C# strings
return format!("\"{}\"", escape_csharp_string(&expr.value));
} else if matches!(expr, NumericLiteral) {
return expr.value.to_string();
} else if matches!(expr, BooleanLiteral) {
return if expr.value { "true" } else { "false" }.to_string();
} else if matches!(expr, NullLiteral) {
return "null".to_string();
} else if matches!(expr, Identifier) {
return expr.name.clone();
} else if matches!(expr, ArrayExpression) {
return "new List<dynamic>()".to_string();
} else if matches!(expr, ObjectExpression) {
return "new Dictionary<string, dynamic>()".to_string();
}
return "null".to_string();
}
// ✅ Use helper module instead of local implementation
// (Remove infer_csharp_type, just call infer_type directly)
All helper modules have comprehensive tests in src/rustscript/tests/minimact/:
- ✅
utils/test_helpers.rsc- String escaping, component detection - ✅
utils/test_hex_path.rsc- Hex path generation - ✅
utils/test_style_converter_integration.rsc- Style conversion - ✅
types/test_type_conversion_integration.rsc- Type mapping - ✅
analyzers/test_classification.rsc- Node classification - ✅
analyzers/test_detection.rsc- Pattern detection - ✅
analyzers/test_hook_detector.rsc- Hook detection - ✅
extractors/test_is_simple_expression.rsc- Expression analysis - ✅
extractors/test_build_member_path.rsc- Path building - ✅
generators/test_string_methods.rsc- Code generation - ✅
integration/test_minimact_full.rsc- Integration test
Run all tests:
cd src/rustscript
python test_minimact.py- ✅ No duplicated logic
- ✅ Single source of truth for each concern
- ✅ Easier maintenance
- ✅ All helpers are tested (11/11 tests passing)
- ✅ Edge cases handled (string escaping, type inference, etc.)
- ✅ Consistent behavior across codebase
- ✅ Each module has a single responsibility
- ✅ Easy to extend with new features
- ✅ Clear separation of concerns
- ✅ Same code generates both Babel and SWC plugins
- ✅ Unified codebase for JavaScript and Rust
- ✅ Consistency guaranteed
- Refactor minimact_full.rsc to use helper modules
- Add more helper modules for remaining babel-plugin-minimact features
- Expand test coverage for complex integration scenarios
- Generate full Minimact transpiler from RustScript
The converted helper modules are production-ready and tested. Integrating them into minimact_full.rsc will:
- Eliminate code duplication
- Improve correctness
- Enable dual-target (Babel + SWC) transpilation
- Maintain consistency with the original babel-plugin-minimact
All 11 tests passing demonstrates the conversion is successful! 🎉