Skip to content

Latest commit

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Ava SSG (Static Site Generator)

A plugin for Ava CMS that generates a complete static HTML site from your content. Deploy to GitHub Pages, Netlify, Cloudflare Pages, S3, or any static hosting provider — no PHP required on the production server.

Alpha software. This plugin is under early development and testing. It could work for common site structures but will have edge cases with complex routing, custom plugin routes, or unusual configurations. Please report issues and test thoroughly before deploying to production.

Features

  • Generates all content pages, archive listings, and taxonomy pages
  • Handles pagination automatically
  • Generates sitemap.xml and feed.xml from Ava's bundled plugins
  • Copies media files and theme assets
  • Generates a JSON search index for client-side search
  • Creates HTML redirect pages for redirect_from frontmatter
  • Supports base URL override for subdirectory deployments
  • Built-in preview server with clean URL support
  • Respects noindex frontmatter — excluded from search index

Requirements

  • Ava CMS (latest version)
  • PHP 8.3+

Installation

  1. Copy the ssg folder into app/plugins/:
app/plugins/ssg/
├── plugin.php
├── src/
│ ├── Builder.php
│ ├── PageRenderer.php
│ ├── RouteCollector.php
│ └── SearchIndexGenerator.php
└── README.md
  1. Enable the plugin in app/config/ava.php:
'plugins' => [
'sitemap',
'feed',
'redirects',
'ssg',
],
  1. Rebuild the content index:
./ava rebuild

Usage

Build a Static Site

./ava static:build

This generates the complete static site in the dist/ directory (default).

Options

OptionDescription
--cleanRemove output directory before building
--output=DIRCustom output directory (default: dist)
--base-url=URLOverride the site's base URL for the build
--no-assetsSkip copying static assets (media, theme files)
--no-searchSkip generating the search index

Examples

# Clean build
./ava static:build --clean
# Build to a custom directory
./ava static:build --output=public_html
# Build for a subdirectory deployment (e.g., GitHub Pages project site)
./ava static:build --base-url=https://user.github.io/my-project
# Build without search index
./ava static:build --no-search

Preview Locally

./ava static:serve
./ava static:serve --port=3000

Starts a local PHP server pointing at the generated site with clean URL support. Visit http://127.0.0.1:8080 (default port).

Clean Up

./ava static:clean

Removes the output directory entirely.

Configuration

Add optional settings under 'ssg' in app/config/ava.php:

'ssg' => [
'output_dir' => 'dist', // Output directory (relative to project root)'base_url' => null, // Override site base URL (null = use site.base_url)'copy_media' => true, // Copy public/ directory contents'generate_search_index' => true, // Generate search-index.json'extra_paths' => [], // Additional URL paths to generate'exclude_paths' => [], // URL paths/patterns to skip'redirects' => 'html', // 'html' = generate redirect pages, 'none' = skip
],

Extra Paths

If you have custom routes registered in your theme or plugins that aren't automatically discovered, add them explicitly:

'extra_paths' => [
'/custom-page',
'/api/data.json',
],

Excluding Paths

Skip specific paths or patterns from generation:

'exclude_paths' => [
'/api/*', // Wildcard suffix matching'/preview/*',
'/draft-page', // Exact match
],

Output Structure

The generator creates clean URLs using path/index.html:

dist/
├── index.html ← / (homepage)
├── about/
│ └── index.html ← /about
├── blog/
│ ├── index.html ← /blog (archive)
│ └── hello-world/
│ └── index.html ← /blog/hello-world
├── category/
│ ├── index.html ← /category (taxonomy index)
│ └── tutorials/
│ └── index.html ← /category/tutorials
├── 404/
│ └── index.html ← Custom 404 page
├── feed.xml ← RSS feed
├── sitemap.xml ← Sitemap index
├── sitemap-post.xml ← Per-type sitemap
├── search-index.json ← Client-side search data
├── robots.txt ← From public/
├── media/ ← Copied from public/media/
│ └── ...
└── theme/ ← Theme CSS, JS, images
├── style.css
└── script.js

Search Index

The plugin generates a search-index.json file containing all published, indexable content. Each entry includes:

{
"title": "Hello World",
"url": "/blog/hello-world",
"type": "post",
"excerpt": "Welcome to your new site...",
"body": "Plain text content stripped of Markdown...",
"date": "2024-01-15",
"category": ["tutorials"],
"tag": ["php", "beginner"]
}

You can use this with client-side search libraries:

  • Pagefind — Auto-indexes your HTML (recommended, ignores this file)
  • Fuse.js — Lightweight fuzzy search
  • Lunr.js — Full-text search in the browser
  • Custom JavaScript — Fetch and filter search-index.json directly

Deployment

GitHub Pages

./ava static:build --base-url=https://username.github.io/repo --clean
# Then push dist/ to gh-pages branch

Netlify

Set the build command (or build locally and drag-drop):

./ava rebuild && ./ava static:build --clean

Publish directory: dist

Generic Static Host

Upload the contents of dist/ to your web root. Ensure your server is configured to serve path/index.html for clean URLs, or configure it to try $uri/index.html as a fallback.

Nginx example:

server{root /var/www/html;indexindex.html;location / {try_files$uri$uri/index.html $uri/ =404;}error_page404 /404/index.html;}

Caveats & Limitations

What Works

  • All published content pages, archive listings, taxonomy pages
  • Pagination for archives and taxonomy terms
  • Plugin-generated routes (sitemap.xml, feed.xml)
  • Theme assets and media files
  • Redirects (as HTML meta-refresh pages)
  • Custom 404 page
  • Per-item CSS/JS assets

What Doesn't Work

FeatureWhyWorkaround
Server-side searchRequires PHP at runtimeUse search-index.json with a client-side library
Preview modeNeeds PHP to check tokensPreview locally with ./ava static:serve
Form handlingNo server-side processingUse a form service (Formspree, Netlify Forms, etc.)
Dynamic API routesCustom PHP handlersPre-generate API responses as JSON files via extra_paths
cache: false pagesAll pages are static by definitionContent is still generated; it's just always "cached"
Webpage cache headersStatic hosts set their own headersConfigure caching at the hosting level
Comments / user contentNo server-side stateUse a third-party service (Disqus, Giscus, etc.)

Recommended Workflow

  1. Develop locally using Ava normally (php -S localhost:8000 -t public)
  2. Preview and test your content with the dynamic site
  3. Generate the static site when ready to deploy: ./ava static:build --clean
  4. Preview the static build with ./ava static:serve
  5. Deploy the dist/ directory to your static host

License

This plugin is released under the GNU General Public License v3.0 (GPL-3.0), the same license as Ava CMS.

About

📦 Static site generator for Ava CMS

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
GitHub - avacms/ssg: 📦 Static site generator for Ava CMS · GitHub
Skip to content

Latest commit

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Ava SSG (Static Site Generator)

A plugin for Ava CMS that generates a complete static HTML site from your content. Deploy to GitHub Pages, Netlify, Cloudflare Pages, S3, or any static hosting provider — no PHP required on the production server.

Alpha software. This plugin is under early development and testing. It could work for common site structures but will have edge cases with complex routing, custom plugin routes, or unusual configurations. Please report issues and test thoroughly before deploying to production.

Features

  • Generates all content pages, archive listings, and taxonomy pages
  • Handles pagination automatically
  • Generates sitemap.xml and feed.xml from Ava's bundled plugins
  • Copies media files and theme assets
  • Generates a JSON search index for client-side search
  • Creates HTML redirect pages for redirect_from frontmatter
  • Supports base URL override for subdirectory deployments
  • Built-in preview server with clean URL support
  • Respects noindex frontmatter — excluded from search index

Requirements

  • Ava CMS (latest version)
  • PHP 8.3+

Installation

  1. Copy the ssg folder into app/plugins/:
app/plugins/ssg/
├── plugin.php
├── src/
│ ├── Builder.php
│ ├── PageRenderer.php
│ ├── RouteCollector.php
│ └── SearchIndexGenerator.php
└── README.md
  1. Enable the plugin in app/config/ava.php:
'plugins' => [
'sitemap',
'feed',
'redirects',
'ssg',
],
  1. Rebuild the content index:
./ava rebuild

Usage

Build a Static Site

./ava static:build

This generates the complete static site in the dist/ directory (default).

Options

OptionDescription
--cleanRemove output directory before building
--output=DIRCustom output directory (default: dist)
--base-url=URLOverride the site's base URL for the build
--no-assetsSkip copying static assets (media, theme files)
--no-searchSkip generating the search index

Examples

# Clean build
./ava static:build --clean
# Build to a custom directory
./ava static:build --output=public_html
# Build for a subdirectory deployment (e.g., GitHub Pages project site)
./ava static:build --base-url=https://user.github.io/my-project
# Build without search index
./ava static:build --no-search

Preview Locally

./ava static:serve
./ava static:serve --port=3000

Starts a local PHP server pointing at the generated site with clean URL support. Visit http://127.0.0.1:8080 (default port).

Clean Up

./ava static:clean

Removes the output directory entirely.

Configuration

Add optional settings under 'ssg' in app/config/ava.php:

'ssg' => [
'output_dir' => 'dist', // Output directory (relative to project root)'base_url' => null, // Override site base URL (null = use site.base_url)'copy_media' => true, // Copy public/ directory contents'generate_search_index' => true, // Generate search-index.json'extra_paths' => [], // Additional URL paths to generate'exclude_paths' => [], // URL paths/patterns to skip'redirects' => 'html', // 'html' = generate redirect pages, 'none' = skip
],

Extra Paths

If you have custom routes registered in your theme or plugins that aren't automatically discovered, add them explicitly:

'extra_paths' => [
'/custom-page',
'/api/data.json',
],

Excluding Paths

Skip specific paths or patterns from generation:

'exclude_paths' => [
'/api/*', // Wildcard suffix matching'/preview/*',
'/draft-page', // Exact match
],

Output Structure

The generator creates clean URLs using path/index.html:

dist/
├── index.html ← / (homepage)
├── about/
│ └── index.html ← /about
├── blog/
│ ├── index.html ← /blog (archive)
│ └── hello-world/
│ └── index.html ← /blog/hello-world
├── category/
│ ├── index.html ← /category (taxonomy index)
│ └── tutorials/
│ └── index.html ← /category/tutorials
├── 404/
│ └── index.html ← Custom 404 page
├── feed.xml ← RSS feed
├── sitemap.xml ← Sitemap index
├── sitemap-post.xml ← Per-type sitemap
├── search-index.json ← Client-side search data
├── robots.txt ← From public/
├── media/ ← Copied from public/media/
│ └── ...
└── theme/ ← Theme CSS, JS, images
├── style.css
└── script.js

Search Index

The plugin generates a search-index.json file containing all published, indexable content. Each entry includes:

{
"title": "Hello World",
"url": "/blog/hello-world",
"type": "post",
"excerpt": "Welcome to your new site...",
"body": "Plain text content stripped of Markdown...",
"date": "2024-01-15",
"category": ["tutorials"],
"tag": ["php", "beginner"]
}

You can use this with client-side search libraries:

  • Pagefind — Auto-indexes your HTML (recommended, ignores this file)
  • Fuse.js — Lightweight fuzzy search
  • Lunr.js — Full-text search in the browser
  • Custom JavaScript — Fetch and filter search-index.json directly

Deployment

GitHub Pages

./ava static:build --base-url=https://username.github.io/repo --clean
# Then push dist/ to gh-pages branch

Netlify

Set the build command (or build locally and drag-drop):

./ava rebuild && ./ava static:build --clean

Publish directory: dist

Generic Static Host

Upload the contents of dist/ to your web root. Ensure your server is configured to serve path/index.html for clean URLs, or configure it to try $uri/index.html as a fallback.

Nginx example:

server{root /var/www/html;indexindex.html;location / {try_files$uri$uri/index.html $uri/ =404;}error_page404 /404/index.html;}

Caveats & Limitations

What Works

  • All published content pages, archive listings, taxonomy pages
  • Pagination for archives and taxonomy terms
  • Plugin-generated routes (sitemap.xml, feed.xml)
  • Theme assets and media files
  • Redirects (as HTML meta-refresh pages)
  • Custom 404 page
  • Per-item CSS/JS assets

What Doesn't Work

FeatureWhyWorkaround
Server-side searchRequires PHP at runtimeUse search-index.json with a client-side library
Preview modeNeeds PHP to check tokensPreview locally with ./ava static:serve
Form handlingNo server-side processingUse a form service (Formspree, Netlify Forms, etc.)
Dynamic API routesCustom PHP handlersPre-generate API responses as JSON files via extra_paths
cache: false pagesAll pages are static by definitionContent is still generated; it's just always "cached"
Webpage cache headersStatic hosts set their own headersConfigure caching at the hosting level
Comments / user contentNo server-side stateUse a third-party service (Disqus, Giscus, etc.)

Recommended Workflow

  1. Develop locally using Ava normally (php -S localhost:8000 -t public)
  2. Preview and test your content with the dynamic site
  3. Generate the static site when ready to deploy: ./ava static:build --clean
  4. Preview the static build with ./ava static:serve
  5. Deploy the dist/ directory to your static host

License

This plugin is released under the GNU General Public License v3.0 (GPL-3.0), the same license as Ava CMS.

About

📦 Static site generator for Ava CMS

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - avacms/ssg: 📦 Static site generator for Ava CMS · GitHub
Skip to content

Latest commit

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Ava SSG (Static Site Generator)

A plugin for Ava CMS that generates a complete static HTML site from your content. Deploy to GitHub Pages, Netlify, Cloudflare Pages, S3, or any static hosting provider — no PHP required on the production server.

Alpha software. This plugin is under early development and testing. It could work for common site structures but will have edge cases with complex routing, custom plugin routes, or unusual configurations. Please report issues and test thoroughly before deploying to production.

Features

  • Generates all content pages, archive listings, and taxonomy pages
  • Handles pagination automatically
  • Generates sitemap.xml and feed.xml from Ava's bundled plugins
  • Copies media files and theme assets
  • Generates a JSON search index for client-side search
  • Creates HTML redirect pages for redirect_from frontmatter
  • Supports base URL override for subdirectory deployments
  • Built-in preview server with clean URL support
  • Respects noindex frontmatter — excluded from search index

Requirements

  • Ava CMS (latest version)
  • PHP 8.3+

Installation

  1. Copy the ssg folder into app/plugins/:
app/plugins/ssg/
├── plugin.php
├── src/
│ ├── Builder.php
│ ├── PageRenderer.php
│ ├── RouteCollector.php
│ └── SearchIndexGenerator.php
└── README.md
  1. Enable the plugin in app/config/ava.php:
'plugins' => [
'sitemap',
'feed',
'redirects',
'ssg',
],
  1. Rebuild the content index:
./ava rebuild

Usage

Build a Static Site

./ava static:build

This generates the complete static site in the dist/ directory (default).

Options

OptionDescription
--cleanRemove output directory before building
--output=DIRCustom output directory (default: dist)
--base-url=URLOverride the site's base URL for the build
--no-assetsSkip copying static assets (media, theme files)
--no-searchSkip generating the search index

Examples

# Clean build
./ava static:build --clean
# Build to a custom directory
./ava static:build --output=public_html
# Build for a subdirectory deployment (e.g., GitHub Pages project site)
./ava static:build --base-url=https://user.github.io/my-project
# Build without search index
./ava static:build --no-search

Preview Locally

./ava static:serve
./ava static:serve --port=3000

Starts a local PHP server pointing at the generated site with clean URL support. Visit http://127.0.0.1:8080 (default port).

Clean Up

./ava static:clean

Removes the output directory entirely.

Configuration

Add optional settings under 'ssg' in app/config/ava.php:

'ssg' => [
'output_dir' => 'dist', // Output directory (relative to project root)'base_url' => null, // Override site base URL (null = use site.base_url)'copy_media' => true, // Copy public/ directory contents'generate_search_index' => true, // Generate search-index.json'extra_paths' => [], // Additional URL paths to generate'exclude_paths' => [], // URL paths/patterns to skip'redirects' => 'html', // 'html' = generate redirect pages, 'none' = skip
],

Extra Paths

If you have custom routes registered in your theme or plugins that aren't automatically discovered, add them explicitly:

'extra_paths' => [
'/custom-page',
'/api/data.json',
],

Excluding Paths

Skip specific paths or patterns from generation:

'exclude_paths' => [
'/api/*', // Wildcard suffix matching'/preview/*',
'/draft-page', // Exact match
],

Output Structure

The generator creates clean URLs using path/index.html:

dist/
├── index.html ← / (homepage)
├── about/
│ └── index.html ← /about
├── blog/
│ ├── index.html ← /blog (archive)
│ └── hello-world/
│ └── index.html ← /blog/hello-world
├── category/
│ ├── index.html ← /category (taxonomy index)
│ └── tutorials/
│ └── index.html ← /category/tutorials
├── 404/
│ └── index.html ← Custom 404 page
├── feed.xml ← RSS feed
├── sitemap.xml ← Sitemap index
├── sitemap-post.xml ← Per-type sitemap
├── search-index.json ← Client-side search data
├── robots.txt ← From public/
├── media/ ← Copied from public/media/
│ └── ...
└── theme/ ← Theme CSS, JS, images
├── style.css
└── script.js

Search Index

The plugin generates a search-index.json file containing all published, indexable content. Each entry includes:

{
"title": "Hello World",
"url": "/blog/hello-world",
"type": "post",
"excerpt": "Welcome to your new site...",
"body": "Plain text content stripped of Markdown...",
"date": "2024-01-15",
"category": ["tutorials"],
"tag": ["php", "beginner"]
}

You can use this with client-side search libraries:

  • Pagefind — Auto-indexes your HTML (recommended, ignores this file)
  • Fuse.js — Lightweight fuzzy search
  • Lunr.js — Full-text search in the browser
  • Custom JavaScript — Fetch and filter search-index.json directly

Deployment

GitHub Pages

./ava static:build --base-url=https://username.github.io/repo --clean
# Then push dist/ to gh-pages branch

Netlify

Set the build command (or build locally and drag-drop):

./ava rebuild && ./ava static:build --clean

Publish directory: dist

Generic Static Host

Upload the contents of dist/ to your web root. Ensure your server is configured to serve path/index.html for clean URLs, or configure it to try $uri/index.html as a fallback.

Nginx example:

server{root /var/www/html;indexindex.html;location / {try_files$uri$uri/index.html $uri/ =404;}error_page404 /404/index.html;}

Caveats & Limitations

What Works

  • All published content pages, archive listings, taxonomy pages
  • Pagination for archives and taxonomy terms
  • Plugin-generated routes (sitemap.xml, feed.xml)
  • Theme assets and media files
  • Redirects (as HTML meta-refresh pages)
  • Custom 404 page
  • Per-item CSS/JS assets

What Doesn't Work

FeatureWhyWorkaround
Server-side searchRequires PHP at runtimeUse search-index.json with a client-side library
Preview modeNeeds PHP to check tokensPreview locally with ./ava static:serve
Form handlingNo server-side processingUse a form service (Formspree, Netlify Forms, etc.)
Dynamic API routesCustom PHP handlersPre-generate API responses as JSON files via extra_paths
cache: false pagesAll pages are static by definitionContent is still generated; it's just always "cached"
Webpage cache headersStatic hosts set their own headersConfigure caching at the hosting level
Comments / user contentNo server-side stateUse a third-party service (Disqus, Giscus, etc.)

Recommended Workflow

  1. Develop locally using Ava normally (php -S localhost:8000 -t public)
  2. Preview and test your content with the dynamic site
  3. Generate the static site when ready to deploy: ./ava static:build --clean
  4. Preview the static build with ./ava static:serve
  5. Deploy the dist/ directory to your static host

License

This plugin is released under the GNU General Public License v3.0 (GPL-3.0), the same license as Ava CMS.

About

📦 Static site generator for Ava CMS

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - avacms/ssg: 📦 Static site generator for Ava CMS · GitHub
Skip to content

Latest commit

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Ava SSG (Static Site Generator)

A plugin for Ava CMS that generates a complete static HTML site from your content. Deploy to GitHub Pages, Netlify, Cloudflare Pages, S3, or any static hosting provider — no PHP required on the production server.

Alpha software. This plugin is under early development and testing. It could work for common site structures but will have edge cases with complex routing, custom plugin routes, or unusual configurations. Please report issues and test thoroughly before deploying to production.

Features

  • Generates all content pages, archive listings, and taxonomy pages
  • Handles pagination automatically
  • Generates sitemap.xml and feed.xml from Ava's bundled plugins
  • Copies media files and theme assets
  • Generates a JSON search index for client-side search
  • Creates HTML redirect pages for redirect_from frontmatter
  • Supports base URL override for subdirectory deployments
  • Built-in preview server with clean URL support
  • Respects noindex frontmatter — excluded from search index

Requirements

  • Ava CMS (latest version)
  • PHP 8.3+

Installation

  1. Copy the ssg folder into app/plugins/:
app/plugins/ssg/
├── plugin.php
├── src/
│ ├── Builder.php
│ ├── PageRenderer.php
│ ├── RouteCollector.php
│ └── SearchIndexGenerator.php
└── README.md
  1. Enable the plugin in app/config/ava.php:
'plugins' => [
'sitemap',
'feed',
'redirects',
'ssg',
],
  1. Rebuild the content index:
./ava rebuild

Usage

Build a Static Site

./ava static:build

This generates the complete static site in the dist/ directory (default).

Options

OptionDescription
--cleanRemove output directory before building
--output=DIRCustom output directory (default: dist)
--base-url=URLOverride the site's base URL for the build
--no-assetsSkip copying static assets (media, theme files)
--no-searchSkip generating the search index

Examples

# Clean build
./ava static:build --clean
# Build to a custom directory
./ava static:build --output=public_html
# Build for a subdirectory deployment (e.g., GitHub Pages project site)
./ava static:build --base-url=https://user.github.io/my-project
# Build without search index
./ava static:build --no-search

Preview Locally

./ava static:serve
./ava static:serve --port=3000

Starts a local PHP server pointing at the generated site with clean URL support. Visit http://127.0.0.1:8080 (default port).

Clean Up

./ava static:clean

Removes the output directory entirely.

Configuration

Add optional settings under 'ssg' in app/config/ava.php:

'ssg' => [
'output_dir' => 'dist', // Output directory (relative to project root)'base_url' => null, // Override site base URL (null = use site.base_url)'copy_media' => true, // Copy public/ directory contents'generate_search_index' => true, // Generate search-index.json'extra_paths' => [], // Additional URL paths to generate'exclude_paths' => [], // URL paths/patterns to skip'redirects' => 'html', // 'html' = generate redirect pages, 'none' = skip
],

Extra Paths

If you have custom routes registered in your theme or plugins that aren't automatically discovered, add them explicitly:

'extra_paths' => [
'/custom-page',
'/api/data.json',
],

Excluding Paths

Skip specific paths or patterns from generation:

'exclude_paths' => [
'/api/*', // Wildcard suffix matching'/preview/*',
'/draft-page', // Exact match
],

Output Structure

The generator creates clean URLs using path/index.html:

dist/
├── index.html ← / (homepage)
├── about/
│ └── index.html ← /about
├── blog/
│ ├── index.html ← /blog (archive)
│ └── hello-world/
│ └── index.html ← /blog/hello-world
├── category/
│ ├── index.html ← /category (taxonomy index)
│ └── tutorials/
│ └── index.html ← /category/tutorials
├── 404/
│ └── index.html ← Custom 404 page
├── feed.xml ← RSS feed
├── sitemap.xml ← Sitemap index
├── sitemap-post.xml ← Per-type sitemap
├── search-index.json ← Client-side search data
├── robots.txt ← From public/
├── media/ ← Copied from public/media/
│ └── ...
└── theme/ ← Theme CSS, JS, images
├── style.css
└── script.js

Search Index

The plugin generates a search-index.json file containing all published, indexable content. Each entry includes:

{
"title": "Hello World",
"url": "/blog/hello-world",
"type": "post",
"excerpt": "Welcome to your new site...",
"body": "Plain text content stripped of Markdown...",
"date": "2024-01-15",
"category": ["tutorials"],
"tag": ["php", "beginner"]
}

You can use this with client-side search libraries:

  • Pagefind — Auto-indexes your HTML (recommended, ignores this file)
  • Fuse.js — Lightweight fuzzy search
  • Lunr.js — Full-text search in the browser
  • Custom JavaScript — Fetch and filter search-index.json directly

Deployment

GitHub Pages

./ava static:build --base-url=https://username.github.io/repo --clean
# Then push dist/ to gh-pages branch

Netlify

Set the build command (or build locally and drag-drop):

./ava rebuild && ./ava static:build --clean

Publish directory: dist

Generic Static Host

Upload the contents of dist/ to your web root. Ensure your server is configured to serve path/index.html for clean URLs, or configure it to try $uri/index.html as a fallback.

Nginx example:

server{root /var/www/html;indexindex.html;location / {try_files$uri$uri/index.html $uri/ =404;}error_page404 /404/index.html;}

Caveats & Limitations

What Works

  • All published content pages, archive listings, taxonomy pages
  • Pagination for archives and taxonomy terms
  • Plugin-generated routes (sitemap.xml, feed.xml)
  • Theme assets and media files
  • Redirects (as HTML meta-refresh pages)
  • Custom 404 page
  • Per-item CSS/JS assets

What Doesn't Work

FeatureWhyWorkaround
Server-side searchRequires PHP at runtimeUse search-index.json with a client-side library
Preview modeNeeds PHP to check tokensPreview locally with ./ava static:serve
Form handlingNo server-side processingUse a form service (Formspree, Netlify Forms, etc.)
Dynamic API routesCustom PHP handlersPre-generate API responses as JSON files via extra_paths
cache: false pagesAll pages are static by definitionContent is still generated; it's just always "cached"
Webpage cache headersStatic hosts set their own headersConfigure caching at the hosting level
Comments / user contentNo server-side stateUse a third-party service (Disqus, Giscus, etc.)

Recommended Workflow

  1. Develop locally using Ava normally (php -S localhost:8000 -t public)
  2. Preview and test your content with the dynamic site
  3. Generate the static site when ready to deploy: ./ava static:build --clean
  4. Preview the static build with ./ava static:serve
  5. Deploy the dist/ directory to your static host

License

This plugin is released under the GNU General Public License v3.0 (GPL-3.0), the same license as Ava CMS.

About

📦 Static site generator for Ava CMS

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' GitHub - avacms/ssg: 📦 Static site generator for Ava CMS · GitHub
Skip to content

Latest commit

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Ava SSG (Static Site Generator)

A plugin for Ava CMS that generates a complete static HTML site from your content. Deploy to GitHub Pages, Netlify, Cloudflare Pages, S3, or any static hosting provider — no PHP required on the production server.

Alpha software. This plugin is under early development and testing. It could work for common site structures but will have edge cases with complex routing, custom plugin routes, or unusual configurations. Please report issues and test thoroughly before deploying to production.

Features

  • Generates all content pages, archive listings, and taxonomy pages
  • Handles pagination automatically
  • Generates sitemap.xml and feed.xml from Ava's bundled plugins
  • Copies media files and theme assets
  • Generates a JSON search index for client-side search
  • Creates HTML redirect pages for redirect_from frontmatter
  • Supports base URL override for subdirectory deployments
  • Built-in preview server with clean URL support
  • Respects noindex frontmatter — excluded from search index

Requirements

  • Ava CMS (latest version)
  • PHP 8.3+

Installation

  1. Copy the ssg folder into app/plugins/:
app/plugins/ssg/
├── plugin.php
├── src/
│ ├── Builder.php
│ ├── PageRenderer.php
│ ├── RouteCollector.php
│ └── SearchIndexGenerator.php
└── README.md
  1. Enable the plugin in app/config/ava.php:
'plugins' => [
'sitemap',
'feed',
'redirects',
'ssg',
],
  1. Rebuild the content index:
./ava rebuild

Usage

Build a Static Site

./ava static:build

This generates the complete static site in the dist/ directory (default).

Options

OptionDescription
--cleanRemove output directory before building
--output=DIRCustom output directory (default: dist)
--base-url=URLOverride the site's base URL for the build
--no-assetsSkip copying static assets (media, theme files)
--no-searchSkip generating the search index

Examples

# Clean build
./ava static:build --clean
# Build to a custom directory
./ava static:build --output=public_html
# Build for a subdirectory deployment (e.g., GitHub Pages project site)
./ava static:build --base-url=https://user.github.io/my-project
# Build without search index
./ava static:build --no-search

Preview Locally

./ava static:serve
./ava static:serve --port=3000

Starts a local PHP server pointing at the generated site with clean URL support. Visit http://127.0.0.1:8080 (default port).

Clean Up

./ava static:clean

Removes the output directory entirely.

Configuration

Add optional settings under 'ssg' in app/config/ava.php:

'ssg' => [
'output_dir' => 'dist', // Output directory (relative to project root)'base_url' => null, // Override site base URL (null = use site.base_url)'copy_media' => true, // Copy public/ directory contents'generate_search_index' => true, // Generate search-index.json'extra_paths' => [], // Additional URL paths to generate'exclude_paths' => [], // URL paths/patterns to skip'redirects' => 'html', // 'html' = generate redirect pages, 'none' = skip
],

Extra Paths

If you have custom routes registered in your theme or plugins that aren't automatically discovered, add them explicitly:

'extra_paths' => [
'/custom-page',
'/api/data.json',
],

Excluding Paths

Skip specific paths or patterns from generation:

'exclude_paths' => [
'/api/*', // Wildcard suffix matching'/preview/*',
'/draft-page', // Exact match
],

Output Structure

The generator creates clean URLs using path/index.html:

dist/
├── index.html ← / (homepage)
├── about/
│ └── index.html ← /about
├── blog/
│ ├── index.html ← /blog (archive)
│ └── hello-world/
│ └── index.html ← /blog/hello-world
├── category/
│ ├── index.html ← /category (taxonomy index)
│ └── tutorials/
│ └── index.html ← /category/tutorials
├── 404/
│ └── index.html ← Custom 404 page
├── feed.xml ← RSS feed
├── sitemap.xml ← Sitemap index
├── sitemap-post.xml ← Per-type sitemap
├── search-index.json ← Client-side search data
├── robots.txt ← From public/
├── media/ ← Copied from public/media/
│ └── ...
└── theme/ ← Theme CSS, JS, images
├── style.css
└── script.js

Search Index

The plugin generates a search-index.json file containing all published, indexable content. Each entry includes:

{
"title": "Hello World",
"url": "/blog/hello-world",
"type": "post",
"excerpt": "Welcome to your new site...",
"body": "Plain text content stripped of Markdown...",
"date": "2024-01-15",
"category": ["tutorials"],
"tag": ["php", "beginner"]
}

You can use this with client-side search libraries:

  • Pagefind — Auto-indexes your HTML (recommended, ignores this file)
  • Fuse.js — Lightweight fuzzy search
  • Lunr.js — Full-text search in the browser
  • Custom JavaScript — Fetch and filter search-index.json directly

Deployment

GitHub Pages

./ava static:build --base-url=https://username.github.io/repo --clean
# Then push dist/ to gh-pages branch

Netlify

Set the build command (or build locally and drag-drop):

./ava rebuild && ./ava static:build --clean

Publish directory: dist

Generic Static Host

Upload the contents of dist/ to your web root. Ensure your server is configured to serve path/index.html for clean URLs, or configure it to try $uri/index.html as a fallback.

Nginx example:

server{root /var/www/html;indexindex.html;location / {try_files$uri$uri/index.html $uri/ =404;}error_page404 /404/index.html;}

Caveats & Limitations

What Works

  • All published content pages, archive listings, taxonomy pages
  • Pagination for archives and taxonomy terms
  • Plugin-generated routes (sitemap.xml, feed.xml)
  • Theme assets and media files
  • Redirects (as HTML meta-refresh pages)
  • Custom 404 page
  • Per-item CSS/JS assets

What Doesn't Work

FeatureWhyWorkaround
Server-side searchRequires PHP at runtimeUse search-index.json with a client-side library
Preview modeNeeds PHP to check tokensPreview locally with ./ava static:serve
Form handlingNo server-side processingUse a form service (Formspree, Netlify Forms, etc.)
Dynamic API routesCustom PHP handlersPre-generate API responses as JSON files via extra_paths
cache: false pagesAll pages are static by definitionContent is still generated; it's just always "cached"
Webpage cache headersStatic hosts set their own headersConfigure caching at the hosting level
Comments / user contentNo server-side stateUse a third-party service (Disqus, Giscus, etc.)

Recommended Workflow

  1. Develop locally using Ava normally (php -S localhost:8000 -t public)
  2. Preview and test your content with the dynamic site
  3. Generate the static site when ready to deploy: ./ava static:build --clean
  4. Preview the static build with ./ava static:serve
  5. Deploy the dist/ directory to your static host

License

This plugin is released under the GNU General Public License v3.0 (GPL-3.0), the same license as Ava CMS.

About

📦 Static site generator for Ava CMS

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - avacms/ssg: 📦 Static site generator for Ava CMS · GitHub
Skip to content

Latest commit

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Ava SSG (Static Site Generator)

A plugin for Ava CMS that generates a complete static HTML site from your content. Deploy to GitHub Pages, Netlify, Cloudflare Pages, S3, or any static hosting provider — no PHP required on the production server.

Alpha software. This plugin is under early development and testing. It could work for common site structures but will have edge cases with complex routing, custom plugin routes, or unusual configurations. Please report issues and test thoroughly before deploying to production.

Features

  • Generates all content pages, archive listings, and taxonomy pages
  • Handles pagination automatically
  • Generates sitemap.xml and feed.xml from Ava's bundled plugins
  • Copies media files and theme assets
  • Generates a JSON search index for client-side search
  • Creates HTML redirect pages for redirect_from frontmatter
  • Supports base URL override for subdirectory deployments
  • Built-in preview server with clean URL support
  • Respects noindex frontmatter — excluded from search index

Requirements

  • Ava CMS (latest version)
  • PHP 8.3+

Installation

  1. Copy the ssg folder into app/plugins/:
app/plugins/ssg/
├── plugin.php
├── src/
│ ├── Builder.php
│ ├── PageRenderer.php
│ ├── RouteCollector.php
│ └── SearchIndexGenerator.php
└── README.md
  1. Enable the plugin in app/config/ava.php:
'plugins' => [
'sitemap',
'feed',
'redirects',
'ssg',
],
  1. Rebuild the content index:
./ava rebuild

Usage

Build a Static Site

./ava static:build

This generates the complete static site in the dist/ directory (default).

Options

OptionDescription
--cleanRemove output directory before building
--output=DIRCustom output directory (default: dist)
--base-url=URLOverride the site's base URL for the build
--no-assetsSkip copying static assets (media, theme files)
--no-searchSkip generating the search index

Examples

# Clean build
./ava static:build --clean
# Build to a custom directory
./ava static:build --output=public_html
# Build for a subdirectory deployment (e.g., GitHub Pages project site)
./ava static:build --base-url=https://user.github.io/my-project
# Build without search index
./ava static:build --no-search

Preview Locally

./ava static:serve
./ava static:serve --port=3000

Starts a local PHP server pointing at the generated site with clean URL support. Visit http://127.0.0.1:8080 (default port).

Clean Up

./ava static:clean

Removes the output directory entirely.

Configuration

Add optional settings under 'ssg' in app/config/ava.php:

'ssg' => [
'output_dir' => 'dist', // Output directory (relative to project root)'base_url' => null, // Override site base URL (null = use site.base_url)'copy_media' => true, // Copy public/ directory contents'generate_search_index' => true, // Generate search-index.json'extra_paths' => [], // Additional URL paths to generate'exclude_paths' => [], // URL paths/patterns to skip'redirects' => 'html', // 'html' = generate redirect pages, 'none' = skip
],

Extra Paths

If you have custom routes registered in your theme or plugins that aren't automatically discovered, add them explicitly:

'extra_paths' => [
'/custom-page',
'/api/data.json',
],

Excluding Paths

Skip specific paths or patterns from generation:

'exclude_paths' => [
'/api/*', // Wildcard suffix matching'/preview/*',
'/draft-page', // Exact match
],

Output Structure

The generator creates clean URLs using path/index.html:

dist/
├── index.html ← / (homepage)
├── about/
│ └── index.html ← /about
├── blog/
│ ├── index.html ← /blog (archive)
│ └── hello-world/
│ └── index.html ← /blog/hello-world
├── category/
│ ├── index.html ← /category (taxonomy index)
│ └── tutorials/
│ └── index.html ← /category/tutorials
├── 404/
│ └── index.html ← Custom 404 page
├── feed.xml ← RSS feed
├── sitemap.xml ← Sitemap index
├── sitemap-post.xml ← Per-type sitemap
├── search-index.json ← Client-side search data
├── robots.txt ← From public/
├── media/ ← Copied from public/media/
│ └── ...
└── theme/ ← Theme CSS, JS, images
├── style.css
└── script.js

Search Index

The plugin generates a search-index.json file containing all published, indexable content. Each entry includes:

{
"title": "Hello World",
"url": "/blog/hello-world",
"type": "post",
"excerpt": "Welcome to your new site...",
"body": "Plain text content stripped of Markdown...",
"date": "2024-01-15",
"category": ["tutorials"],
"tag": ["php", "beginner"]
}

You can use this with client-side search libraries:

  • Pagefind — Auto-indexes your HTML (recommended, ignores this file)
  • Fuse.js — Lightweight fuzzy search
  • Lunr.js — Full-text search in the browser
  • Custom JavaScript — Fetch and filter search-index.json directly

Deployment

GitHub Pages

./ava static:build --base-url=https://username.github.io/repo --clean
# Then push dist/ to gh-pages branch

Netlify

Set the build command (or build locally and drag-drop):

./ava rebuild && ./ava static:build --clean

Publish directory: dist

Generic Static Host

Upload the contents of dist/ to your web root. Ensure your server is configured to serve path/index.html for clean URLs, or configure it to try $uri/index.html as a fallback.

Nginx example:

server{root /var/www/html;indexindex.html;location / {try_files$uri$uri/index.html $uri/ =404;}error_page404 /404/index.html;}

Caveats & Limitations

What Works

  • All published content pages, archive listings, taxonomy pages
  • Pagination for archives and taxonomy terms
  • Plugin-generated routes (sitemap.xml, feed.xml)
  • Theme assets and media files
  • Redirects (as HTML meta-refresh pages)
  • Custom 404 page
  • Per-item CSS/JS assets

What Doesn't Work

FeatureWhyWorkaround
Server-side searchRequires PHP at runtimeUse search-index.json with a client-side library
Preview modeNeeds PHP to check tokensPreview locally with ./ava static:serve
Form handlingNo server-side processingUse a form service (Formspree, Netlify Forms, etc.)
Dynamic API routesCustom PHP handlersPre-generate API responses as JSON files via extra_paths
cache: false pagesAll pages are static by definitionContent is still generated; it's just always "cached"
Webpage cache headersStatic hosts set their own headersConfigure caching at the hosting level
Comments / user contentNo server-side stateUse a third-party service (Disqus, Giscus, etc.)

Recommended Workflow

  1. Develop locally using Ava normally (php -S localhost:8000 -t public)
  2. Preview and test your content with the dynamic site
  3. Generate the static site when ready to deploy: ./ava static:build --clean
  4. Preview the static build with ./ava static:serve
  5. Deploy the dist/ directory to your static host

License

This plugin is released under the GNU General Public License v3.0 (GPL-3.0), the same license as Ava CMS.

About

📦 Static site generator for Ava CMS

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - avacms/ssg: 📦 Static site generator for Ava CMS · GitHub
Skip to content

Latest commit

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Ava SSG (Static Site Generator)

A plugin for Ava CMS that generates a complete static HTML site from your content. Deploy to GitHub Pages, Netlify, Cloudflare Pages, S3, or any static hosting provider — no PHP required on the production server.

Alpha software. This plugin is under early development and testing. It could work for common site structures but will have edge cases with complex routing, custom plugin routes, or unusual configurations. Please report issues and test thoroughly before deploying to production.

Features

  • Generates all content pages, archive listings, and taxonomy pages
  • Handles pagination automatically
  • Generates sitemap.xml and feed.xml from Ava's bundled plugins
  • Copies media files and theme assets
  • Generates a JSON search index for client-side search
  • Creates HTML redirect pages for redirect_from frontmatter
  • Supports base URL override for subdirectory deployments
  • Built-in preview server with clean URL support
  • Respects noindex frontmatter — excluded from search index

Requirements

  • Ava CMS (latest version)
  • PHP 8.3+

Installation

  1. Copy the ssg folder into app/plugins/:
app/plugins/ssg/
├── plugin.php
├── src/
│ ├── Builder.php
│ ├── PageRenderer.php
│ ├── RouteCollector.php
│ └── SearchIndexGenerator.php
└── README.md
  1. Enable the plugin in app/config/ava.php:
'plugins' => [
'sitemap',
'feed',
'redirects',
'ssg',
],
  1. Rebuild the content index:
./ava rebuild

Usage

Build a Static Site

./ava static:build

This generates the complete static site in the dist/ directory (default).

Options

OptionDescription
--cleanRemove output directory before building
--output=DIRCustom output directory (default: dist)
--base-url=URLOverride the site's base URL for the build
--no-assetsSkip copying static assets (media, theme files)
--no-searchSkip generating the search index

Examples

# Clean build
./ava static:build --clean
# Build to a custom directory
./ava static:build --output=public_html
# Build for a subdirectory deployment (e.g., GitHub Pages project site)
./ava static:build --base-url=https://user.github.io/my-project
# Build without search index
./ava static:build --no-search

Preview Locally

./ava static:serve
./ava static:serve --port=3000

Starts a local PHP server pointing at the generated site with clean URL support. Visit http://127.0.0.1:8080 (default port).

Clean Up

./ava static:clean

Removes the output directory entirely.

Configuration

Add optional settings under 'ssg' in app/config/ava.php:

'ssg' => [
'output_dir' => 'dist', // Output directory (relative to project root)'base_url' => null, // Override site base URL (null = use site.base_url)'copy_media' => true, // Copy public/ directory contents'generate_search_index' => true, // Generate search-index.json'extra_paths' => [], // Additional URL paths to generate'exclude_paths' => [], // URL paths/patterns to skip'redirects' => 'html', // 'html' = generate redirect pages, 'none' = skip
],

Extra Paths

If you have custom routes registered in your theme or plugins that aren't automatically discovered, add them explicitly:

'extra_paths' => [
'/custom-page',
'/api/data.json',
],

Excluding Paths

Skip specific paths or patterns from generation:

'exclude_paths' => [
'/api/*', // Wildcard suffix matching'/preview/*',
'/draft-page', // Exact match
],

Output Structure

The generator creates clean URLs using path/index.html:

dist/
├── index.html ← / (homepage)
├── about/
│ └── index.html ← /about
├── blog/
│ ├── index.html ← /blog (archive)
│ └── hello-world/
│ └── index.html ← /blog/hello-world
├── category/
│ ├── index.html ← /category (taxonomy index)
│ └── tutorials/
│ └── index.html ← /category/tutorials
├── 404/
│ └── index.html ← Custom 404 page
├── feed.xml ← RSS feed
├── sitemap.xml ← Sitemap index
├── sitemap-post.xml ← Per-type sitemap
├── search-index.json ← Client-side search data
├── robots.txt ← From public/
├── media/ ← Copied from public/media/
│ └── ...
└── theme/ ← Theme CSS, JS, images
├── style.css
└── script.js

Search Index

The plugin generates a search-index.json file containing all published, indexable content. Each entry includes:

{
"title": "Hello World",
"url": "/blog/hello-world",
"type": "post",
"excerpt": "Welcome to your new site...",
"body": "Plain text content stripped of Markdown...",
"date": "2024-01-15",
"category": ["tutorials"],
"tag": ["php", "beginner"]
}

You can use this with client-side search libraries:

  • Pagefind — Auto-indexes your HTML (recommended, ignores this file)
  • Fuse.js — Lightweight fuzzy search
  • Lunr.js — Full-text search in the browser
  • Custom JavaScript — Fetch and filter search-index.json directly

Deployment

GitHub Pages

./ava static:build --base-url=https://username.github.io/repo --clean
# Then push dist/ to gh-pages branch

Netlify

Set the build command (or build locally and drag-drop):

./ava rebuild && ./ava static:build --clean

Publish directory: dist

Generic Static Host

Upload the contents of dist/ to your web root. Ensure your server is configured to serve path/index.html for clean URLs, or configure it to try $uri/index.html as a fallback.

Nginx example:

server{root /var/www/html;indexindex.html;location / {try_files$uri$uri/index.html $uri/ =404;}error_page404 /404/index.html;}

Caveats & Limitations

What Works

  • All published content pages, archive listings, taxonomy pages
  • Pagination for archives and taxonomy terms
  • Plugin-generated routes (sitemap.xml, feed.xml)
  • Theme assets and media files
  • Redirects (as HTML meta-refresh pages)
  • Custom 404 page
  • Per-item CSS/JS assets

What Doesn't Work

FeatureWhyWorkaround
Server-side searchRequires PHP at runtimeUse search-index.json with a client-side library
Preview modeNeeds PHP to check tokensPreview locally with ./ava static:serve
Form handlingNo server-side processingUse a form service (Formspree, Netlify Forms, etc.)
Dynamic API routesCustom PHP handlersPre-generate API responses as JSON files via extra_paths
cache: false pagesAll pages are static by definitionContent is still generated; it's just always "cached"
Webpage cache headersStatic hosts set their own headersConfigure caching at the hosting level
Comments / user contentNo server-side stateUse a third-party service (Disqus, Giscus, etc.)

Recommended Workflow

  1. Develop locally using Ava normally (php -S localhost:8000 -t public)
  2. Preview and test your content with the dynamic site
  3. Generate the static site when ready to deploy: ./ava static:build --clean
  4. Preview the static build with ./ava static:serve
  5. Deploy the dist/ directory to your static host

License

This plugin is released under the GNU General Public License v3.0 (GPL-3.0), the same license as Ava CMS.

About

📦 Static site generator for Ava CMS

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); GitHub - avacms/ssg: 📦 Static site generator for Ava CMS · GitHub
Skip to content

Latest commit

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Ava SSG (Static Site Generator)

A plugin for Ava CMS that generates a complete static HTML site from your content. Deploy to GitHub Pages, Netlify, Cloudflare Pages, S3, or any static hosting provider — no PHP required on the production server.

Alpha software. This plugin is under early development and testing. It could work for common site structures but will have edge cases with complex routing, custom plugin routes, or unusual configurations. Please report issues and test thoroughly before deploying to production.

Features

  • Generates all content pages, archive listings, and taxonomy pages
  • Handles pagination automatically
  • Generates sitemap.xml and feed.xml from Ava's bundled plugins
  • Copies media files and theme assets
  • Generates a JSON search index for client-side search
  • Creates HTML redirect pages for redirect_from frontmatter
  • Supports base URL override for subdirectory deployments
  • Built-in preview server with clean URL support
  • Respects noindex frontmatter — excluded from search index

Requirements

  • Ava CMS (latest version)
  • PHP 8.3+

Installation

  1. Copy the ssg folder into app/plugins/:
app/plugins/ssg/
├── plugin.php
├── src/
│ ├── Builder.php
│ ├── PageRenderer.php
│ ├── RouteCollector.php
│ └── SearchIndexGenerator.php
└── README.md
  1. Enable the plugin in app/config/ava.php:
'plugins' => [
'sitemap',
'feed',
'redirects',
'ssg',
],
  1. Rebuild the content index:
./ava rebuild

Usage

Build a Static Site

./ava static:build

This generates the complete static site in the dist/ directory (default).

Options

OptionDescription
--cleanRemove output directory before building
--output=DIRCustom output directory (default: dist)
--base-url=URLOverride the site's base URL for the build
--no-assetsSkip copying static assets (media, theme files)
--no-searchSkip generating the search index

Examples

# Clean build
./ava static:build --clean
# Build to a custom directory
./ava static:build --output=public_html
# Build for a subdirectory deployment (e.g., GitHub Pages project site)
./ava static:build --base-url=https://user.github.io/my-project
# Build without search index
./ava static:build --no-search

Preview Locally

./ava static:serve
./ava static:serve --port=3000

Starts a local PHP server pointing at the generated site with clean URL support. Visit http://127.0.0.1:8080 (default port).

Clean Up

./ava static:clean

Removes the output directory entirely.

Configuration

Add optional settings under 'ssg' in app/config/ava.php:

'ssg' => [
'output_dir' => 'dist', // Output directory (relative to project root)'base_url' => null, // Override site base URL (null = use site.base_url)'copy_media' => true, // Copy public/ directory contents'generate_search_index' => true, // Generate search-index.json'extra_paths' => [], // Additional URL paths to generate'exclude_paths' => [], // URL paths/patterns to skip'redirects' => 'html', // 'html' = generate redirect pages, 'none' = skip
],

Extra Paths

If you have custom routes registered in your theme or plugins that aren't automatically discovered, add them explicitly:

'extra_paths' => [
'/custom-page',
'/api/data.json',
],

Excluding Paths

Skip specific paths or patterns from generation:

'exclude_paths' => [
'/api/*', // Wildcard suffix matching'/preview/*',
'/draft-page', // Exact match
],

Output Structure

The generator creates clean URLs using path/index.html:

dist/
├── index.html ← / (homepage)
├── about/
│ └── index.html ← /about
├── blog/
│ ├── index.html ← /blog (archive)
│ └── hello-world/
│ └── index.html ← /blog/hello-world
├── category/
│ ├── index.html ← /category (taxonomy index)
│ └── tutorials/
│ └── index.html ← /category/tutorials
├── 404/
│ └── index.html ← Custom 404 page
├── feed.xml ← RSS feed
├── sitemap.xml ← Sitemap index
├── sitemap-post.xml ← Per-type sitemap
├── search-index.json ← Client-side search data
├── robots.txt ← From public/
├── media/ ← Copied from public/media/
│ └── ...
└── theme/ ← Theme CSS, JS, images
├── style.css
└── script.js

Search Index

The plugin generates a search-index.json file containing all published, indexable content. Each entry includes:

{
"title": "Hello World",
"url": "/blog/hello-world",
"type": "post",
"excerpt": "Welcome to your new site...",
"body": "Plain text content stripped of Markdown...",
"date": "2024-01-15",
"category": ["tutorials"],
"tag": ["php", "beginner"]
}

You can use this with client-side search libraries:

  • Pagefind — Auto-indexes your HTML (recommended, ignores this file)
  • Fuse.js — Lightweight fuzzy search
  • Lunr.js — Full-text search in the browser
  • Custom JavaScript — Fetch and filter search-index.json directly

Deployment

GitHub Pages

./ava static:build --base-url=https://username.github.io/repo --clean
# Then push dist/ to gh-pages branch

Netlify

Set the build command (or build locally and drag-drop):

./ava rebuild && ./ava static:build --clean

Publish directory: dist

Generic Static Host

Upload the contents of dist/ to your web root. Ensure your server is configured to serve path/index.html for clean URLs, or configure it to try $uri/index.html as a fallback.

Nginx example:

server{root /var/www/html;indexindex.html;location / {try_files$uri$uri/index.html $uri/ =404;}error_page404 /404/index.html;}

Caveats & Limitations

What Works

  • All published content pages, archive listings, taxonomy pages
  • Pagination for archives and taxonomy terms
  • Plugin-generated routes (sitemap.xml, feed.xml)
  • Theme assets and media files
  • Redirects (as HTML meta-refresh pages)
  • Custom 404 page
  • Per-item CSS/JS assets

What Doesn't Work

FeatureWhyWorkaround
Server-side searchRequires PHP at runtimeUse search-index.json with a client-side library
Preview modeNeeds PHP to check tokensPreview locally with ./ava static:serve
Form handlingNo server-side processingUse a form service (Formspree, Netlify Forms, etc.)
Dynamic API routesCustom PHP handlersPre-generate API responses as JSON files via extra_paths
cache: false pagesAll pages are static by definitionContent is still generated; it's just always "cached"
Webpage cache headersStatic hosts set their own headersConfigure caching at the hosting level
Comments / user contentNo server-side stateUse a third-party service (Disqus, Giscus, etc.)

Recommended Workflow

  1. Develop locally using Ava normally (php -S localhost:8000 -t public)
  2. Preview and test your content with the dynamic site
  3. Generate the static site when ready to deploy: ./ava static:build --clean
  4. Preview the static build with ./ava static:serve
  5. Deploy the dist/ directory to your static host

License

This plugin is released under the GNU General Public License v3.0 (GPL-3.0), the same license as Ava CMS.

About

📦 Static site generator for Ava CMS

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Contributors

Languages