Overview
Clovie is a modern yet minimalist static site generator that emphasizes sensible defaults, modularity, and speed. It supports multiple template engines, bundles JavaScript, compiles SCSS, copies assets, and ships with a development server with live reload.
Core Features
- Template Engine Agnostic: Handlebars, Nunjucks, Pug, Mustache, or custom.
- Asset Processing: JavaScript bundling with esbuild, SCSS compilation, static asset copying.
- Development Server: Live reload with Browser‑Sync and file watching.
- Data-Driven Pages: Model system for dynamic page generation.
- Pagination Support: Built‑in pagination for data‑driven content.
Usage
New Project
Using Clovie CLI (Recommended)
# Create a new project
npx clovie create my-site
# Or with global install
clovie create my-site
Installation
Option 1: Local Installation (Recommended)
npm install --save-dev clovie
# Use via npm scripts
npm run build
npm run dev
Option 2: Global Installation
npm install -g clovie
Building and Development
Build the site
# Global
clovie build
# or
npm run build
Dev/Watch
# Global
clovie watch
# or
npm run dev
Configuration
Minimal Configuration (Recommended)
Clovie uses smart defaults and auto‑detection, so you can start with just:
export default {
data: {
title: 'My Site'
}
};
Full Configuration
export default {
// Custom paths (optional - Clovie will auto-detect if not specified)
scripts: './src/js/app.js',
styles: './src/css/main.scss',
views: './templates',
assets: './public',
outputDir: './build',
// Your data
data: {
title: 'My Site'
},
// Custom compiler (optional - Clovie has a good default)
compiler: (template, data) => {
return yourTemplateEngine(template, data);
}
};
Advanced Features
Async Data Loading
export default {
// ... other config
data: async () => {
// Fetch data from API
const response = await fetch('https://api.example.com/posts');
const posts = await response.json();
return {
title: 'My Blog',
posts: posts,
timestamp: new Date().toISOString()
};
}
};
Data Models & Dynamic Pages
export default {
// ... other config
data: {
title: 'My Blog',
posts: [
{ id: 1, title: 'First Post', content: 'Hello World' },
{ id: 2, title: 'Second Post', content: 'Another post' },
{ id: 3, title: 'Third Post', content: 'Yet another' }
]
},
models: {
posts: {
template: '_post.html', // Template to use
paginate: 2, // Posts per page (optional)
output: (post, index) => { // Custom output filename
return `post-${post.id}.html`;
},
transform: (post, index) => { // Transform data before rendering
return {
...post,
excerpt: post.content.substring(0, 100) + '...',
date: new Date().toISOString()
};
}
}
}
};
Data Transformation
export default {
// ... other config
models: {
products: {
template: '_product.html',
transform: (product, index) => {
return {
...product,
price: `$${product.price.toFixed(2)}`,
slug: product.name.toLowerCase().replace(/\s+/g, '-'),
inStock: product.quantity > 0
};
}
}
}
};
Pagination
export default {
// ... other config
models: {
blog: {
template: '_blog.html',
paginate: 5, // 5 posts per page
output: (posts, pageNum) => {
return pageNum === 0 ? 'blog.html' : `blog-${pageNum + 1}.html`;
}
}
}
};
Output:
blog.html
– First 5 postsblog-2.html
– Next 5 postsblog-3.html
– Remaining posts
Template (_post.html
):
<!DOCTYPE html>
<html>
<head>
<title> - </title>
</head>
<body>
<article>
<h1></h1>
<p></p>
<div></div>
</article>
</body>
</html>
Output:
post-1.html
– First post pagepost-2.html
– Second post pagepost-3.html
– Third post page
Template Engines
Clovie is template‑engine agnostic. Examples for popular engines:
Handlebars
import Handlebars from 'handlebars';
export default {
// ... other config
compiler: (template, data) => {
const compiled = Handlebars.compile(template);
return compiled(data);
}
};
Nunjucks
import nunjucks from 'nunjucks';
export default {
// ... other config
compiler: (template, data) => {
return nunjucks.renderString(template, data);
}
};
Pug
import pug from 'pug';
export default {
// ... other config
compiler: (template, data) => {
return pug.render(template, { ...data, pretty: true });
}
};
Custom Engine
export default {
// ... other config
compiler: (template, data) => {
// Simple variable replacement
return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
return data[key] || match;
});
}
};
Error Handling & Best Practices
Error Handling
- Missing directories: Handles missing views, scripts, or assets folders.
- File read errors: Continues processing even if individual files fail.
- Template errors: Clear error messages for compilation failures.
- Data validation: Warns about invalid data structures.
Progress Indicators
🚀 Starting build...
🧹 Cleaning output directory...
📊 Loading data...
Loaded 2 data sources
📝 Processing views...
Processed 5 views
🎨 Rendering templates...
Rendered 5 templates
⚡ Bundling scripts...
Bundled 1 script files
🎨 Compiling styles...
Compiled 1 style files
📦 Processing assets...
Processed 3 asset files
💾 Writing files...
✅ Build completed in 45ms
Auto‑Discovery
🔍 Auto-detected views directory: views
🔍 Auto-detected scripts entry: scripts/main.js
🔍 Auto-detected styles entry: styles/main.scss
🔍 Auto-detected assets directory: assets
Best Practices
- Use partial templates (files starting with
_
) for reusable components. - Validate data structures before passing to models.
- Handle async data with proper error catching.
- Use meaningful output filenames for SEO and organization.
- Transform data in the model configuration, not in templates.
Development
# Install dependencies
npm install
# Run tests
npm test
# Run tests in watch mode
npm run test:watch
Project Structure
packages/clovie/
├── __tests__/ # Test files
│ └── index.test.js
├── bin/ # CLI executable
│ └── cli.js
├── config/ # Configuration files
│ └── default.config.js
├── lib/ # Source code
│ ├── core/ # Core functionality
│ │ ├── index.js # Main Clovie class
│ │ ├── bundler.js # JavaScript bundling
│ │ ├── render.js # Template rendering
│ │ ├── write.js # File writing
│ │ ├── getViews.js # View processing
│ │ ├── getData.js # Data loading
│ │ ├── getStyles.js # SCSS compilation
│ │ └── getAssets.js # Asset processing
│ └── utils/ # Utility functions
│ ├── clean.js # Directory cleaning
│ └── create.js # Project creation
└── package.json
Troubleshooting
Common Issues
"Views directory does not exist"
- Ensure the
views
path in your config is correct. - Create the
views
directory if it does not exist.
"Data for model must be an array"
- Check that your data structure matches the model configuration.
- Ensure the referenced data key contains an array.
"Maximum directory depth exceeded"
- Check for circular symlinks or extremely deep directory structures.
- The limit is 50 levels deep (configurable in code).
Build failures
- Check console output for specific error messages.
- Verify all referenced files exist.
- Ensure template syntax matches your compiler.
License
MIT
Auto‑detection includes:
views/
directory for HTML templatesscripts/main.js
for JavaScript entry pointstyles/main.scss
for SCSS entry pointassets/
directory for static files