minilive
v0.1.0
Published
Server-driven UI framework with live updates and seamless state management
Maintainers
Readme
MiniLive
Server-driven UI framework with live updates and seamless state management.
Installation
npm install miniliveQuick Start
- Create your project structure:
my-app/
├── index.js
├── pages/
│ └── home.mhtml
└── logic/
└── home.js- Create
index.js:
const minilive = require('minilive');
const server = minilive({
port: 3000
});
server.serve();- Create
pages/home.mhtml:
<!DOCTYPE html>
<html>
<head>
<title>{{title}}</title>
</head>
<body>
<h1>{{message}}</h1>
<button onclick="triggerEvent('click')">Click me</button>
</body>
</html>- Create
logic/home.js:
output = {
title: 'MiniLive Demo',
message: 'Hello World!'
};
if (input.event === 'click') {
output.message = 'Button clicked!';
}- Run:
node index.jsVisit http://localhost:3000/pages/home
What's Included
When you install minilive, you get:
- Express server with WebSocket support
- Morphdom for efficient DOM updates
- Session management with cookies
- SPA navigation without page reloads
- Automatic script injection
- State persistence across refreshes
All client-side assets (morphdom, client.js) are served automatically from the package.
Advanced Options
Custom Command Handler
Extend the command system with your own commands:
const server = minilive({
commandHandler: async (cmd, { socket, res }) => {
if (cmd.type === 'customCommand') {
// Handle your custom command
return true; // Return true to skip built-in processing
}
return false; // Let built-in handler process it
}
});Template Rewriter
Preprocess templates before rendering:
const server = minilive({
templateRewriter: (template, { page, data, sessionId }) => {
// Modify template here
return template.replace('{{customTag}}', 'replaced');
}
});Page-Specific Includes
Add CSS and JavaScript files per page:
const server = minilive({
includes: (page) => {
if (page === 'dashboard') {
return [
'/css/dashboard.css',
'https://cdn.example.com/charts.js',
{ type: 'js', src: '/js/analytics.js', defer: true }
];
}
return [];
}
});Prepend/Postpend JavaScript
Add JavaScript code that runs before or after your logic scripts:
const server = minilive({
prepend: `
// This code runs before every logic script
const helpers = {
formatDate: (date) => new Date(date).toLocaleDateString(),
capitalize: (str) => str.charAt(0).toUpperCase() + str.slice(1)
};
// Make helpers available to all logic scripts
const h = helpers;
`,
postpend: `
// This code runs after every logic script
if (output.debug) {
console.log('Debug output:', output);
}
// Add timestamp to all responses
output._timestamp = new Date().toISOString();
`
});This feature is useful for:
- Sharing common utilities across all logic scripts
- Adding debugging or logging functionality
- Injecting global configuration or constants
- Post-processing output data
- Setting up common error handlers
Example logic script using prepended helpers:
// logic/profile.js
output = {
username: input.username,
joinDate: h.formatDate(input.joinDate), // Uses prepended helper
displayName: h.capitalize(input.username)
};Partials
MiniLive supports Mustache partials for reusable template components. Partials allow you to break your templates into smaller, reusable pieces.
Basic Usage
Create partial files in your pages directory with the .mhtml extension:
pages/
├── home.mhtml
├── header.mhtml # Partial
├── footer.mhtml # Partial
└── components/
└── user-card.mhtml # Nested partialReference partials in your templates using {{> partialName}}:
<!-- pages/home.mhtml -->
<!DOCTYPE html>
<html>
<body>
{{> header}}
<main>
<h1>{{title}}</h1>
{{#users}}
{{> components/user-card}}
{{/users}}
</main>
{{> footer}}
</body>
</html><!-- pages/header.mhtml -->
<header>
<nav>
<a href="/">Home</a>
{{#isLoggedIn}}
<span>Welcome, {{username}}!</span>
{{/isLoggedIn}}
</nav>
</header><!-- pages/components/user-card.mhtml -->
<div class="user-card">
<img src="{{avatar}}" alt="{{name}}" />
<h3>{{name}}</h3>
<p>{{bio}}</p>
</div>Important Notes
- File Resolution: Partials are resolved relative to the
pagesDirdirectory - Extension: The
.mhtmlextension is automatically added - don't include it in the partial reference - Nested Partials: Partials can include other partials (recursive loading is supported)
- Error Handling: Missing partials will throw an error with the expected file path
- Data Context: Partials inherit the data context from their parent template
Logic Script Requirements
Your logic scripts must provide data for both the main template AND all referenced partials:
// logic/home.js
output = {
// Main template data
title: 'My App',
// Header partial data
isLoggedIn: true,
username: 'John Doe',
// User cards data
users: [
{
name: 'Alice',
avatar: '/images/alice.jpg',
bio: 'Frontend Developer'
},
{
name: 'Bob',
avatar: '/images/bob.jpg',
bio: 'Backend Developer'
}
],
// Footer partial data
copyrightYear: new Date().getFullYear(),
companyName: 'My Company'
};Testing
testLogic Function
Test your page logic without running the full server:
const { testLogic } = require('minilive');
// Test a page with JSON input
const result = await testLogic('home', {
event: 'click',
userId: 123
});
console.log(result); // Returns the output object from the logic scriptParameters:
pageName: Page name without.jsextension (e.g., 'login', 'dashboard')jsonInput: JSON object to pass asinputto the logic scriptoptions: Optional config object withlogicDir(defaults to./logic)
Example test:
const { testLogic } = require('minilive');
async function testHomePage() {
try {
// Test initial load
const initialState = await testLogic('home', { event: 'onLoad' });
console.log('Initial state:', initialState);
// Test button click
const clickState = await testLogic('home', { event: 'click' });
console.log('After click:', clickState);
} catch (error) {
console.error('Test failed:', error.message);
}
}
testHomePage();This function uses the same VM execution environment as the live server, ensuring your tests match production behavior exactly.
