Skip to main content

Development Workflow

Learn how to develop, test, and deploy Wunderframe applications with a streamlined workflow that emphasizes productivity and zero configuration.

Development Environment

Wunderframe provides a rich development environment with hot reloading, live editing, and integrated debugging tools.

Starting Development

# Start the development server
wunder dev

# Or with specific port
wunder dev --port 3000

# Enable debug mode
wunder dev --debug

The development server provides:

  • 🔥 Hot Reloading - Automatic page refresh on file changes
  • ⚡ Live Compilation - LUAT templates compile instantly
  • 🎨 CSS Watching - TailwindCSS rebuilds on style changes
  • 📱 Live Editing - Visual editor integration
  • 🐛 Debug Mode - Detailed error messages and stack traces

File Watching

The development server automatically watches for changes in:

my-app/
├── lib/ # LUAT templates and Lua modules
├── src/ # Frontend JavaScript and CSS
├── app.lua # Application entry point
├── app.js # Frontend entry point
├── app.css # Global styles
└── package.json # Dependencies

Live Editing Integration

Wunderframe integrates with the visual editor for real-time content updates:

// app.js - Live editing setup
document.addEventListener('htmx:load', () => {
if(window.raisin) {
const editor = window.raisin.editor;

// Listen for content updates from the editor
editor.onMessage("UPDATE", (newData) => {
htmx.ajax('POST', location.href, {
target: '#app',
swap: 'outerHTML',
values: {
properties: JSON.stringify(newData.properties)
}
});
});

// Send current page data to editor
editor.sendMessage("INIT", {
content: getPageContent(),
contentType: getCurrentContentType()
});
}
});

Building for Production

Production Build

# Create optimized production build
wunder build

# Build with specific target
wunder build --target /dist

# Enable production optimizations
wunder build --optimize

The build process:

  1. Compiles LUAT templates to optimized Lua bytecode
  2. Bundles JavaScript with esbuild for minimal size
  3. Processes CSS with TailwindCSS purging unused styles
  4. Optimizes assets with compression and caching headers
  5. Generates static files for CDN deployment

Build Output

dist/
├── static/ # Static assets (CSS, JS, images)
├── lua/ # Compiled Lua modules
├── app.lua # Main application file
└── wunder.toml # Production configuration

Testing

Unit Testing

Test your Lua modules and LUAT templates:

-- tests/content_types_test.lua
local lu = require('luaunit')
local pageType = require('lib/content_types/page')

function TestPageRender()
local node = {
properties = {
title = "Test Page",
content = "Hello World"
}
}

local runtime = createTestRuntime()
local result = pageType.render(node, runtime)

lu.assertStrContains(result, "Test Page")
lu.assertStrContains(result, "Hello World")
end

os.exit(lu.LuaUnit.run())

Run tests:

# Run all tests
wunder test

# Run specific test file
wunder test tests/content_types_test.lua

# Run with coverage
wunder test --coverage

Integration Testing

Test complete workflows with the test server:

-- tests/integration_test.lua
local testServer = require('wunder.test_server')

function TestHomepage()
local server = testServer.start()
local response = server.get('/')

lu.assertEquals(response.status, 200)
lu.assertStrContains(response.body, "Welcome")

server.stop()
end

Frontend Testing

Test client-side functionality:

// tests/frontend.test.js
import { test, expect } from '@playwright/test';

test('homepage loads correctly', async ({ page }) => {
await page.goto('/');
await expect(page.locator('h1')).toContainText('Welcome');
});

test('interactive elements work', async ({ page }) => {
await page.goto('/');
await page.click('[data-testid="toggle-button"]');
await expect(page.locator('[data-testid="content"]')).toBeVisible();
});

Deployment

Platform Deployment

Wunderframe applications can be deployed to various platforms:

Wunderframe Cloud

# Deploy to Wunderframe Cloud
wunder deploy

# Deploy to specific environment
wunder deploy --env production

# Deploy with environment variables
wunder deploy --env production --var API_KEY=secret

Docker Deployment

# Dockerfile
FROM wunderframe/runtime:latest

COPY . /app
WORKDIR /app

RUN wunder build --optimize

EXPOSE 8080
CMD ["wunder", "serve"]
# Build and run container
docker build -t my-app .
docker run -p 8080:8080 my-app

Static Site Generation

# Generate static site
wunder generate

# Generate with custom routes
wunder generate --routes routes.json

# Generate for CDN deployment
wunder generate --cdn

Environment Configuration

Configure different environments using wunder.toml:

# wunder.toml
[environments.development]
debug = true
hot_reload = true
database_url = "sqlite:dev.db"

[environments.staging]
debug = false
database_url = "postgres://staging-db"
cdn_url = "https://staging-cdn.example.com"

[environments.production]
debug = false
optimize = true
database_url = "postgres://prod-db"
cdn_url = "https://cdn.example.com"
cache_ttl = 3600

Performance Optimization

LUAT Template Optimization

-- Compile templates ahead of time
local compiled = luat.precompile("lib/pages/product.luat")

-- Cache compiled templates
local templateCache = {}

local function getCachedTemplate(path)
if not templateCache[path] then
templateCache[path] = luat.compile(path)
end
return templateCache[path]
end

Asset Optimization

// app.js - Lazy load heavy components
const loadHeavyComponent = async () => {
const { default: Component } = await import('./src/components/Heavy.js');
return Component;
};

// Use intersection observer for lazy loading
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
loadHeavyComponent().then(Component => {
Component.mount(entry.target);
});
}
});
});

Caching Strategy

-- app.lua - Implement caching
local cache = require("wunder.cache")

local function render(pageContext, runtime)
local cacheKey = pageContext.full_path

-- Try cache first
local cached = cache.get(cacheKey)
if cached then
return cached
end

-- Render and cache result
local result = renderPage(pageContext, runtime)
cache.set(cacheKey, result, 300) -- 5 minute TTL

return result
end

Debugging

Debug Mode

Enable detailed debugging information:

# Start with debug mode
wunder dev --debug

# Show SQL queries
wunder dev --debug --sql

# Enable Lua stack traces
wunder dev --debug --lua-trace

Logging

-- Use built-in logging
local log = require("wunder.log")

local function render(node, runtime)
log.info("Rendering node: " .. node.id)
log.debug("Node properties: " .. json.encode(node.properties))

-- Log performance
local startTime = os.clock()
local result = luat.compile(template, data, runtime)
local endTime = os.clock()

log.info("Template rendered in " .. (endTime - startTime) .. "s")

return result
end

Error Handling

-- Graceful error handling
local function render(node, runtime)
local success, result = pcall(function()
return luat.compile("lib/pages/complex.luat", node.properties, runtime)
end)

if not success then
log.error("Template compilation failed: " .. result)
-- Fallback to simple template
return luat.compile("lib/pages/error.luat", {
error = result,
node = node
}, runtime)
end

return result
end

CI/CD Integration

GitHub Actions

# .github/workflows/deploy.yml
name: Deploy to Production

on:
push:
branches: [main]

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: wunderframe/setup-wunder@v1
- run: wunder test

deploy:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: wunderframe/setup-wunder@v1
- run: wunder deploy --env production
env:
WUNDER_TOKEN: ${{ secrets.WUNDER_TOKEN }}

Automated Testing

# Test workflow
name: Test

on: [push, pull_request]

jobs:
test:
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
lua-version: [5.4, luajit]

runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v3
- uses: wunderframe/setup-wunder@v1
with:
lua-version: ${{ matrix.lua-version }}
- run: wunder test --coverage
- uses: codecov/codecov-action@v3

What's Next?