Genairus logoGenAI-R-Us
Genairus logoGenAI-R-Us

Flux Generator Implementation Guide

Version 1.0

Overview

Flux generators transform your universal UI definitions into framework-specific implementations. This guide covers how generators work, how to build custom generators, and best practices for extending Flux to support new platforms.


Generator Architecture

Core Components

Every Flux generator consists of four main components:

  1. Parser - Converts .flux files into an Abstract Syntax Tree (AST)
  2. Validator - Ensures semantic correctness and type safety
  3. Transformer - Maps universal Flux concepts to framework-specific patterns
  4. Emitter - Generates idiomatic code for the target framework
┌─────────────┐     ┌──────────────┐     ┌─────────────┐     ┌─────────────┐
│  .flux file │ ──> │    Parser    │ ──> │  Validator  │ ──> │ Transformer │
└─────────────┘     └──────────────┘     └─────────────┘     └─────────────┘
                                                                      │
                                                                      ▼
                                                              ┌─────────────┐
                                                              │   Emitter   │
                                                              └─────────────┘
                                                                      │
                                                                      ▼
                                                         ┌──────────────────────┐
                                                         │ Framework-native code│
                                                         └──────────────────────┘

The Flux AST

Node Types

The Flux AST represents your UI model as a tree of typed nodes:

interface FluxAST {
  components: ComponentNode[]
  designTokens?: DesignTokenNode
  imports?: ImportNode[]
}

interface ComponentNode {
  name: string
  props?: PropDefinition[]
  state?: StateDefinition[]
  layout?: LayoutNode
  styles?: StyleNode
  effects?: EffectNode[]
  handlers?: HandlerNode[]
}

interface LayoutNode {
  type: 'VStack' | 'HStack' | 'ZStack' | 'Grid' | 'List'
  properties: LayoutProperty[]
  children: (ComponentNode | LayoutNode)[]
}

Example AST

For this Flux component:

component Button {
  props: {
    label: String
    onClick: Handler
  }

  layout: HStack {
    children: [
      Text { text: props.label }
    ]
  }
}

The AST looks like:

{
  "type": "Component",
  "name": "Button",
  "props": [
    {
      "name": "label",
      "type": "String",
      "required": true
    },
    {
      "name": "onClick",
      "type": "Handler",
      "required": true
    }
  ],
  "layout": {
    "type": "HStack",
    "children": [
      {
        "type": "Text",
        "properties": {
          "text": {
            "type": "PropReference",
            "path": "props.label"
          }
        }
      }
    ]
  }
}

Building a Generator

Step 1: Set Up Your Project

# Create a new generator project
mkdir flux-generator-my-framework
cd flux-generator-my-framework
npm init -y

# Install Flux core dependencies
npm install @flux/core @flux/parser @flux/types

Step 2: Implement the Generator Interface

import { FluxGenerator, FluxAST, GeneratorOptions } from '@flux/core';

export class MyFrameworkGenerator implements FluxGenerator {
  name = 'my-framework';
  version = '1.0.0';

  // Define what Flux features this generator supports
  capabilities = {
    layouts: ['VStack', 'HStack', 'Grid'],
    primitives: ['Text', 'Button', 'Image'],
    stateManagement: true,
    effects: true,
    navigation: false  // Not supported yet
  };

  async generate(ast: FluxAST, options: GeneratorOptions): Promise<string> {
    // Transform the AST into framework-specific code
    const components = ast.components.map(comp => this.generateComponent(comp));
    return components.join('\n\n');
  }

  private generateComponent(node: ComponentNode): string {
    const props = this.generateProps(node.props);
    const layout = this.generateLayout(node.layout);

    return `
      export function ${node.name}(${props}) {
        ${layout}
      }
    `;
  }

  private generateProps(props: PropDefinition[]): string {
    // Map Flux types to framework types
    return props.map(p => `${p.name}: ${this.mapType(p.type)}`).join(', ');
  }

  private mapType(fluxType: string): string {
    const typeMap = {
      'String': 'string',
      'Integer': 'number',
      'Boolean': 'boolean',
      'Handler': '() => void'
    };
    return typeMap[fluxType] || 'any';
  }

  private generateLayout(layout: LayoutNode): string {
    // Convert Flux layout to framework-specific markup
    switch(layout.type) {
      case 'VStack':
        return this.generateVStack(layout);
      case 'HStack':
        return this.generateHStack(layout);
      default:
        throw new Error(`Unsupported layout: ${layout.type}`);
    }
  }

  private generateVStack(layout: LayoutNode): string {
    // Framework-specific implementation
    return `<div class="flex flex-col">...</div>`;
  }
}

Step 3: Register Your Generator

// flux.config.ts
import { MyFrameworkGenerator } from './generator';

export default {
  generators: {
    'my-framework': MyFrameworkGenerator
  }
};

Framework-Specific Patterns

React Generator

The React generator produces functional components with hooks:

class ReactGenerator implements FluxGenerator {
  generateComponent(node: ComponentNode): string {
    const propsInterface = this.generatePropsInterface(node);
    const stateHooks = this.generateStateHooks(node.state);
    const effectHooks = this.generateEffectHooks(node.effects);
    const jsx = this.generateJSX(node.layout);

    return `
      ${propsInterface}

      export function ${node.name}(props: ${node.name}Props) {
        ${stateHooks}
        ${effectHooks}

        return (
          ${jsx}
        );
      }
    `;
  }

  private generateStateHooks(state: StateDefinition[]): string {
    return state.map(s =>
      `const [${s.name}, set${capitalize(s.name)}] = useState(${s.initialValue});`
    ).join('\n');
  }

  private generateJSX(layout: LayoutNode): string {
    // Map Flux layouts to JSX
    const children = layout.children.map(c => this.generateJSX(c)).join('\n');

    switch(layout.type) {
      case 'VStack':
        return `<div className="flex flex-col">${children}</div>`;
      case 'HStack':
        return `<div className="flex flex-row">${children}</div>`;
      default:
        return children;
    }
  }
}

SwiftUI Generator

The SwiftUI generator produces native Swift views:

class SwiftUIGenerator implements FluxGenerator {
  generateComponent(node: ComponentNode): string {
    const props = this.generateProperties(node.props);
    const stateVars = this.generateStateVars(node.state);
    const body = this.generateBody(node.layout);

    return `
      struct ${node.name}: View {
        ${props}
        ${stateVars}

        var body: some View {
          ${body}
        }
      }
    `;
  }

  private generateStateVars(state: StateDefinition[]): string {
    return state.map(s =>
      `@State private var ${s.name}: ${this.mapType(s.type)} = ${s.initialValue}`
    ).join('\n');
  }

  private generateBody(layout: LayoutNode): string {
    const children = layout.children.map(c => this.generateBody(c)).join('\n');

    switch(layout.type) {
      case 'VStack':
        return `VStack(spacing: ${layout.spacing || 0}) {\n${children}\n}`;
      case 'HStack':
        return `HStack(spacing: ${layout.spacing || 0}) {\n${children}\n}`;
      default:
        return children;
    }
  }
}

Jetpack Compose Generator

The Jetpack Compose generator produces Kotlin composables:

class JetpackComposeGenerator implements FluxGenerator {
  generateComponent(node: ComponentNode): string {
    const params = this.generateParameters(node.props);
    const stateVars = this.generateRememberStates(node.state);
    const composable = this.generateComposable(node.layout);

    return `
      @Composable
      fun ${node.name}(${params}) {
        ${stateVars}

        ${composable}
      }
    `;
  }

  private generateRememberStates(state: StateDefinition[]): string {
    return state.map(s =>
      `var ${s.name} by remember { mutableStateOf(${s.initialValue}) }`
    ).join('\n');
  }

  private generateComposable(layout: LayoutNode): string {
    const children = layout.children.map(c => this.generateComposable(c)).join('\n');

    switch(layout.type) {
      case 'VStack':
        return `Column(verticalArrangement = Arrangement.spacedBy(${layout.spacing || 0}.dp)) {\n${children}\n}`;
      case 'HStack':
        return `Row(horizontalArrangement = Arrangement.spacedBy(${layout.spacing || 0}.dp)) {\n${children}\n}`;
      default:
        return children;
    }
  }
}

Handling Unsupported Features

Generators should gracefully handle features they don't support:

Emit Warnings

class MyGenerator implements FluxGenerator {
  validate(ast: FluxAST): ValidationResult {
    const warnings: string[] = [];

    ast.components.forEach(comp => {
      // Check for unsupported features
      if (comp.navigation && !this.capabilities.navigation) {
        warnings.push(
          `Component "${comp.name}" uses navigation, which is not supported by ${this.name}`
        );
      }
    });

    return { valid: true, warnings };
  }
}

Provide Fallbacks

private generateLayout(layout: LayoutNode): string {
  if (layout.type === 'Grid' && !this.supportsGrid()) {
    // Fallback to flexbox
    console.warn('Grid layout not supported, falling back to flexbox');
    return this.generateFlexboxGrid(layout);
  }

  return this.generateNativeGrid(layout);
}

Emit Compile Errors for Critical Features

class MyGenerator implements FluxGenerator {
  validate(ast: FluxAST): ValidationResult {
    const errors: string[] = [];

    ast.components.forEach(comp => {
      if (comp.uses3DTransforms && !this.supports3D) {
        errors.push(
          `Component "${comp.name}" uses 3D transforms which cannot be implemented in ${this.name}`
        );
      }
    });

    return {
      valid: errors.length === 0,
      errors
    };
  }
}

Testing Your Generator

Unit Tests

import { parseFlux } from '@flux/parser';
import { MyFrameworkGenerator } from './generator';

describe('MyFrameworkGenerator', () => {
  it('should generate a basic component', () => {
    const flux = `
      component Button {
        props: {
          label: String
        }
      }
    `;

    const ast = parseFlux(flux);
    const generator = new MyFrameworkGenerator();
    const output = generator.generate(ast);

    expect(output).toContain('export function Button');
    expect(output).toContain('label: string');
  });

  it('should handle state correctly', () => {
    const flux = `
      component Counter {
        state: {
          count: Integer = 0
        }
      }
    `;

    const ast = parseFlux(flux);
    const generator = new MyFrameworkGenerator();
    const output = generator.generate(ast);

    expect(output).toContain('useState');
    expect(output).toContain('count');
  });
});

Integration Tests

describe('MyFrameworkGenerator Integration', () => {
  it('should generate a working component', async () => {
    const flux = readFluxFile('fixtures/Button.flux');
    const generator = new MyFrameworkGenerator();
    const output = await generator.generate(parseFlux(flux));

    // Write output to temp directory
    writeFile('/tmp/Button.tsx', output);

    // Try to compile it
    const result = await compile('/tmp/Button.tsx');
    expect(result.success).toBe(true);
    expect(result.errors).toHaveLength(0);
  });
});

Best Practices

1. Maintain Semantic Fidelity

Generate code that preserves the intent of the Flux definition:

// Flux definition
component Card {
  layout: VStack {
    spacing: 16
    padding: 20
    alignment: "center"
  }
}

Good (preserves semantics):

<div className="flex flex-col items-center gap-4 p-5">
  {/* spacing: 16px = gap-4, padding: 20px = p-5 */}
</div>

Bad (loses semantic meaning):

<div style={{ display: 'flex', flexDirection: 'column' }}>
  {/* No spacing, padding, or alignment */}
</div>

2. Generate Idiomatic Code

Make generated code feel native to the framework:

React: Use hooks, functional components SwiftUI: Use property wrappers, view builders Jetpack Compose: Use composables, remember states

3. Preserve Type Safety

Always generate type-safe code:

// Generate TypeScript interfaces for props
interface ButtonProps {
  label: string;
  variant: 'primary' | 'secondary';
  onClick: () => void;
}

4. Support Incremental Adoption

Allow developers to mix Flux-generated components with hand-written code:

// Generated component should be importable and composable
import { Button } from './generated/Button';

// Hand-written component
export function LoginForm() {
  return (
    <form>
      <Button label="Log In" onClick={handleLogin} />
    </form>
  );
}

5. Provide Source Maps

Generate source maps so debugging traces back to .flux files:

class MyGenerator implements FluxGenerator {
  generateWithSourceMap(ast: FluxAST): GeneratedOutput {
    const output = this.generate(ast);
    const sourceMap = this.createSourceMap(ast, output);

    return { code: output, map: sourceMap };
  }
}

Publishing Your Generator

Package Structure

flux-generator-my-framework/
├── src/
│   ├── generator.ts
│   ├── transformer.ts
│   └── emitter.ts
├── tests/
│   └── generator.test.ts
├── fixtures/
│   └── *.flux
├── package.json
└── README.md

package.json

{
  "name": "@flux/generator-my-framework",
  "version": "1.0.0",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "keywords": ["flux", "generator", "ui", "my-framework"],
  "peerDependencies": {
    "@flux/core": "^1.0.0"
  }
}

Register with Flux Registry

# Publish to npm
npm publish

# Register with Flux
flux generator register @flux/generator-my-framework

Further Reading