Personal Project Notice: This is a personal, experimental project and has not yet been fully tested for production use. The implementation is a learning exercise and proof-of-concept. Use at your own risk.
Woovy Framework is a modern, React-based static site generator (SSG) designed for performance, flexibility, and developer experience. Inspired by Gatsby and Next.js, it provides a powerful GraphQL data layer, extensible plugin architecture, and build-time optimizations for generating lightning-fast static websites.
Woovy Framework is a comprehensive static site generator that:
- Generates optimized static HTML at build time for maximum performance
- Leverages React 18+ with modern hooks and concurrent features
- Provides a unified GraphQL data layer for querying data from multiple sources
- Features an extensible plugin system with lifecycle hooks for customization
- Includes Next.js-style features like API routes and ISR (Incremental Static Regeneration)
| Feature | Benefit |
|---|---|
| GraphQL Data Layer | Unified interface for querying filesystem, APIs, and databases |
| Plugin Ecosystem | Extend functionality with source, transformer, and utility plugins |
| Performance First | Automatic code splitting, lazy loading, image optimization |
| Developer Experience | Hot module replacement, error boundaries, comprehensive debugging |
| Modern Architecture | Webpack 5, React 18, TypeScript support |
- Static Site Generator (SSG) - Generates optimized HTML at build time
- React 18+ - Modern React with hooks and concurrent features
- GraphQL Data Layer - Unified query interface for all data sources
- Webpack 5 - Industry-standard bundler with code splitting
- Plugin System - Extensible lifecycle hooks and APIs
- Node System - In-memory node store with createNode API
- GraphQL Integration - Auto-generated schema from data sources
- Source Plugins - Filesystem, REST API, custom sources
- Transformer Plugins - JSON, Markdown, YAML transformation
- File-based Routing - Automatic route creation from
src/pages/ - Dynamic Pages - Programmatic page generation via API
- SSR Support - Server-side rendering with hydration
- Route Prefetching - Instant navigation with preloader
- Code Splitting - Automatic per-route bundles
- Lazy Loading - On-demand component loading
- Image Optimization - WebP, AVIF generation with sharp
- Incremental Builds - Only rebuild changed files
# Clone the repository
git clone https://github.com/cbuntingde/woovy-framework.git
cd woovy-framework
# Install dependencies
npm install
# Start development server
npm run develop# Use the create command
npm run create my-site
# Or manually create the structure
mkdir my-site && cd my-site
npm init -y
npm install woovy-frameworkwoovy-framework/
├── bin/ # CLI executables
│ ├── cli.js # Main CLI entry
│ ├── develop.js # Development server
│ ├── build.js # Production build
│ ├── serve.js # Static file server
│ └── create.js # Project scaffolding
├── src/ # Source code
│ ├── api/ # API route handling
│ ├── build/ # Build pipeline & optimization
│ ├── components/ # React components
│ ├── dev-server/ # Development server
│ ├── graphql/ # GraphQL data layer
│ ├── node/ # Node system & storage
│ ├── pages/ # Page routing & generation
│ ├── plugins/ # Plugin system & plugins
│ ├── templates/ # Page templates
│ ├── utils/ # Utilities
│ ├── index.js # Main entry point
│ ├── ssr.js # SSR handling
│ └── router.js # Client-side routing
├── public/ # Static assets (generated)
├── cache/ # Build cache
├── docs/ # Documentation
├── __tests__/ # Test suites
├── types/ # TypeScript definitions
├── woovy-config.js # Framework configuration
├── woovy-node.js # Node API extensions
├── woovy-browser.js # Browser-specific config
├── woovy-ssr.js # SSR-specific config
├── webpack.config.js # Webpack configuration
└── package.json # Dependencies
module.exports = {
siteMetadata: {
title: 'My Woovy Site',
description: 'A static site built with Woovy Framework',
author: 'Your Name',
siteUrl: 'https://example.com',
},
plugins: [
// Add plugins here
],
};module.exports = {
siteMetadata: {
title: 'My Blog',
},
plugins: [
{
resolve: 'woovy-source-filesystem',
options: {
path: './content/blog',
name: 'blog',
},
},
{
resolve: 'woovy-transformer-markdown',
options: {
plugins: [
{
resolve: 'woovy-remark-images',
options: {
maxWidth: 800,
},
},
],
},
},
],
};// woovy-node.js
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions;
const result = await graphql(`
query {
allMarkdownRemark {
nodes {
id
frontmatter {
slug
}
}
}
}
`);
result.data.allMarkdownRemark.nodes.forEach(node => {
createPage({
path: `/blog/${node.frontmatter.slug}`,
component: require.resolve('./src/templates/blog-post.js'),
context: {
id: node.id,
},
});
});
};// woovy-source-my-api/index.js
exports.sourceNodes = async ({ actions, createNode, createContentDigest }, config) => {
const { apiUrl, apiKey } = config;
const response = await fetch(`${apiUrl}/posts`, {
headers: { Authorization: `Bearer ${apiKey}` }
});
const posts = await response.json();
for (const post of posts) {
const node = {
id: `post-${post.id}`,
parent: null,
children: [],
internal: {
type: 'BlogPost',
contentDigest: createContentDigest(post),
},
...post,
};
createNode(node);
}
};// woovy-transformer-csv/index.js
const fs = require('fs');
const csv = require('csv-parse');
exports.onCreateNode = async ({ node, actions, createNode, createContentDigest }) => {
if (node.extension !== 'csv') return;
const content = fs.readFileSync(node.absolutePath, 'utf8');
// Parse CSV and create nodes
const records = await parseCsv(content);
for (const record of records) {
const csvNode = {
id: `csv-${node.id}-${record.id}`,
parent: node.id,
children: [],
internal: {
type: 'CsvRecord',
contentDigest: createContentDigest(record),
},
...record,
_file: node.absolutePath,
};
createNode(csvNode);
}
};| Hook | Phase | Description |
|---|---|---|
onPreBootstrap |
Bootstrap | Before bootstrap starts |
onPostBootstrap |
Bootstrap | After bootstrap completes |
sourceNodes |
Bootstrap | Source data into node system |
onCreateNode |
Transform | When a node is created |
onCreateSchemaCustomization |
Schema | Customize GraphQL schema |
onCreateWebpackConfig |
Build | Modify webpack configuration |
createPages |
Pages | Create pages programmatically |
onCreatePage |
Pages | When a page is created |
onPostBuild |
Build | After build completes |
// Create a node
createNode({
id: 'unique-id',
parent: null,
children: [],
internal: {
type: 'MyType',
contentDigest: createContentDigest(data),
},
// Custom fields
field: 'value',
});
// Add field to existing node
createNodeField({
node,
fieldName: 'slug',
fieldValue: 'my-slug',
});// Create a page
createPage({
path: '/about',
component: require.resolve('./src/templates/about.js'),
context: {
id: '123',
},
});
// Create a redirect
createRedirect({
fromPath: '/old-page',
toPath: '/new-page',
isPermanent: true,
});| Command | Description |
|---|---|
npm run develop |
Start development server |
npm run build |
Build for production |
npm run serve |
Serve production build |
npm run create |
Create new project |
npm test |
Run test suite |
npm run lint |
Run linter |
npm run typecheck |
Run TypeScript checker |
npm run security:audit |
Run security audit |
# Run all tests
npm test
# Run tests in watch mode
npm run test:watch
# Run with coverage
npm test -- --coverageContributions are welcome! Please read our contributing guidelines first.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
For AI agents working with Woovy Framework, see the AI Installer Guide for comprehensive documentation on architecture, plugin development, and best practices.
For security vulnerabilities, please email cbuntingde@gmail.com directly rather than using the public issue tracker.
MIT License - see LICENSE for details.
Created by Chris Bunting · cbuntingde@gmail.com