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:
- Compiles LUAT templates to optimized Lua bytecode
- Bundles JavaScript with esbuild for minimal size
- Processes CSS with TailwindCSS purging unused styles
- Optimizes assets with compression and caching headers
- 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?
- Content Types - Managing different page types
- Built-in Functions - Available Lua functions
- Project Structure - Detailed file organization