Clovie

A Node.js-based static site generator designed to be simple, fast, and highly modular. Simple but deep, easy to start with room to grow.

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

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

Back to top

Configuration

Minimal Configuration (Recommended)

Clovie uses smart defaults and auto‑detection, so you can start with just:

export default {
  data: {
    title: 'My Site'
  }
};

Auto‑detection includes:

  • views/ directory for HTML templates
  • scripts/main.js for JavaScript entry point
  • styles/main.scss for SCSS entry point
  • assets/ directory for static files

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);
  }
};

Back to top

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 posts
  • blog-2.html – Next 5 posts
  • blog-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:

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;
    });
  }
};

Back to top

Error Handling & Best Practices

Error Handling

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

  1. Use partial templates (files starting with _) for reusable components.
  2. Validate data structures before passing to models.
  3. Handle async data with proper error catching.
  4. Use meaningful output filenames for SEO and organization.
  5. Transform data in the model configuration, not in templates.

Back to top

Development

# Install dependencies
npm install

# Run tests
npm test

# Run tests in watch mode
npm run test:watch

Back to top

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

Back to top

Troubleshooting

Common Issues

"Views directory does not exist"

"Data for model must be an array"

"Maximum directory depth exceeded"

Build failures

Back to top

License

MIT

Back to top