funky-monkey/static-site-miso

Lightweight PHP static site generator with Twig templates, Markdown sources, collections, and pagination.

Maintainers

Package info

github.com/funky-monkey/static-site-miso

pkg:composer/funky-monkey/static-site-miso

Statistics

Installs: 5

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 2

v0.2.5 2026-04-15 12:58 UTC

This package is auto-updated.

Last update: 2026-05-15 13:14:43 UTC


README

This is a PHP-based static site generator inspired by HydePHP and Jekyll. It compiles Markdown sources into HTML using Twig templates, understands directory-driven collections (with pagination), and copies asset folders like your legacy Jekyll css/ directory.

Features

  • Markdown → HTML via league/commonmark
  • YAML front matter with slug/date inference
  • Twig layouts and partials (drop-in replacements for Liquid templates)
  • Collection support with configurable pagination and permalinks
  • Asset directory copying (defaults to css/)
  • Built-in preview server (miso run) with optional --watch flag that polls for changes and rebuilds automatically — no extra dependencies required
  • --sitemap — generates a sitemap.xml following the sitemaps.org spec, with <loc> and <lastmod> for every rendered page
  • --robots — generates a robots.txt with a Sitemap: directive pointing to your sitemap
  • --llms — generates a llms.txt in the llmstxt.org format, a plain-text index of all pages grouped by collection for LLM consumption
  • Schema.org JSON-LD injection — drop a *-schema.yml file in _config/ to activate structured data for any page or collection
  • Environment-aware builds — --env=NAME loads .env.NAME and substitutes ${VAR} tokens in site.yaml and menu.yaml at build time; exposes vars as env in all Twig templates
  • Single PHP CLI (miso) that you can install locally or globally

Installation

Prerequisites

  • PHP 8.2+ with the intl extension enabled
  • Composer

Global CLI install via Packagist (recommended)

Install Miso globally so the miso command is available everywhere on your machine:

composer global require funky-monkey/static-site-miso

Ensure Composer’s global bin directory is on your PATH (typically ~/.composer/vendor/bin or ~/Library/Application Support/composer/vendor/bin on macOS):

export PATH="$HOME/.composer/vendor/bin:$PATH"

Add the line above to your shell profile (for example ~/.zshrc) so it persists.

You can now run miso build from any project. Use miso --help to see all available options.

To update to the latest version:

composer global update funky-monkey/static-site-miso

To uninstall:

composer global remove funky-monkey/static-site-miso

Project-local install (from source)

# Clone the repository, then from the project root:
composer install

# Build the demo site
php bin/miso build

The generated HTML will appear in _site/. Open _site/index.html in your browser to preview.

Project Layout

content/           # Markdown sources (subdirectories become collections)
css/               # Legacy CSS or other assets copied verbatim
templates/         # Twig templates (override defaults as needed)
_config/site.yaml  # Site metadata, SEO defaults, and collection settings
_config/menu.yaml  # Optional navigation structure
_site/             # Build output (generated)

Configuration

_config/site.yaml controls site metadata, SEO defaults, paths, and collection behaviour:

site:
  title: "My Site"
  description: "Static site powered by Miso"
  base_url: "https://example.com"
  seo:
    author: "Jane Doe"
    default_keywords:
      - "docs"
      - "product"
    canonical: "https://example.com"
    social_image: "/media/social-card.png"
    open_graph:
      title: "Docs Overview"
      description: "Learn everything about our product"
      image: "https://example.com/media/og-card.png"
      url: "https://example.com/docs/"
      type: "website"
      locale: "en_US"
      site_name: "Example Docs"
    twitter:
      card: "summary_large_image"
      site: "@example"
      creator: "@janedoe"
      title: "Example Docs"
      description: "Documentation & tutorials"
      image: "https://example.com/media/twitter-card.png"
paths:
  content: "content"
  templates: "templates"
  output: "_site"
  assets:
    - "css"
collections:
  blog:
    path: "content/blog"
    pagination:
      per_page: 5
    layout: "collection-item.twig.html"
    list_layout: "collection.twig.html"
    permalink: "/blog/{slug}/"
    list_permalink: "/blog/"

Add a new collection by creating another subdirectory in content/ (for example content/projects/) and mirroring it in the collections: section if you need custom layouts or pagination settings. Provide a _config/menu.yaml to manage navigation; by default menus.primary is rendered as the main menu.

Environment Variables

Use --env=NAME to load a .env.NAME file from the project root. After YAML parsing, Miso substitutes ${VAR} tokens in both site.yaml and menu.yaml. This is the recommended way to handle staging vs production URLs without committing secrets.

.env.production

SITE_URL=https://example.com
APP_URL=https://app.example.com

.env.staging

SITE_URL=https://staging.example.com
APP_URL=https://staging.app.example.com

_config/site.yaml

site:
  base_url: "${SITE_URL}"

_config/menu.yaml

actions:
  - title: "Login"
    url: "${APP_URL}/login/"
    style: "outline"

Rules:

  • Miso hard-fails with a clear error if --env=NAME is given but .env.NAME does not exist — prevents silent wrong-URL builds on CI
  • If --env is omitted, Miso falls back to a plain .env file; if that is also absent, no substitution occurs and unresolved tokens remain as-is
  • Env vars are also available in all Twig templates as {{ env.VAR_NAME }}
  • Commit a .env.example with dummy values; gitignore .env.* except .env.example
miso build --env=staging
miso build --env=production --sitemap --robots
miso run --env=staging --watch

Usage

Create a new project anywhere with:

miso new my-site

If you run miso new inside an empty directory, scaffolding is placed in the current folder. Add --force to overwrite non-empty directories.

Then run the build from your project root:

miso build

This clears _site/, renders pages into HTML, generates paginated collection listings, and copies asset directories.

Pass optional flags to generate additional output files:

miso build --sitemap   # generate sitemap.xml
miso build --robots    # generate robots.txt
miso build --llms      # generate llms.txt
miso build --rss       # generate feed.xml (RSS 2.0)
miso build --search    # generate search.json
miso build --sitemap --robots --llms --rss  # all four together

Use --env to load environment-specific variables (see Environment Variables):

miso build --env=staging
miso build --env=production --sitemap --robots

Override defaults when needed:

miso build --root=/path/to/project --config=/path/to/custom-site.yaml

For a quick preview server (auto-build + PHP’s built-in server):

miso run
# or specify port/host
miso run --port=9000 --host=0.0.0.0

Add --watch to automatically rebuild whenever source files change:

miso run --watch
# combine with port/host as needed
miso run --watch --port=9000 --host=0.0.0.0

With --watch enabled, Miso polls for changes every 0.5 seconds across the following directories:

Directory File types watched
content/ .md, .markdown
templates/ .html, .twig.html
css/ .css
_config/ .yaml

When a change is detected, the full site is rebuilt in place and the running PHP server picks up the new files immediately. A timestamp is printed for each rebuild cycle:

[14:23:01] Changes detected. Rebuilding...
[14:23:01] Build complete.

Note: --watch uses polling, not filesystem events, so it works across all platforms and network file systems without any additional dependencies. There is no browser live-reload; refresh the page manually after a rebuild.

The --sitemap, --robots, --llms, and --env flags also work with miso run and miso run --watch:

miso run --watch --sitemap --robots --llms --rss
miso run --watch --env=staging

Generated Files

Flag Output file Description
--sitemap _site/sitemap.xml Standard XML sitemap (sitemaps.org spec). Includes all rendered pages with <loc> and, when available, <lastmod> from front matter date. Requires site.base_url in _config/site.yaml.
--robots _site/robots.txt Robots exclusion file (User-agent: * / Allow: /). Appends a Sitemap: directive when site.base_url is set.
--llms _site/llms.txt Plain-text site index in llmstxt.org format. Lists all pages grouped by collection with titles, URLs, and descriptions.
--rss _site/feed.xml RSS 2.0 feed of all dated collection items, newest first. Validates against the W3C Feed Validator. Requires site.base_url in _config/site.yaml.
--search _site/search.json JSON search index compatible with Fuse.js and Lunr. Each entry includes title, url, description, content (HTML stripped), and date.

Further reading

Full documentation is available in the wiki: