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:
- Parser - Converts
.fluxfiles into an Abstract Syntax Tree (AST) - Validator - Ensures semantic correctness and type safety
- Transformer - Maps universal Flux concepts to framework-specific patterns
- 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