Metadata-Version: 2.4
Name: medusa-ssg
Version: 0.1.0
Summary: Minimal Python static site generator. Markdown + Jinja2, Tailwind CSS built-in, zero configuration, no frontmatter.
Author: wusher
License: MIT
Project-URL: Homepage, https://github.com/yourname/medusa-ssg
Project-URL: Repository, https://github.com/yourname/medusa-ssg
Project-URL: Issues, https://github.com/yourname/medusa-ssg/issues
Project-URL: Documentation, https://github.com/yourname/medusa-ssg#readme
Keywords: static-site-generator,ssg,markdown,jinja2,tailwind,blog,website
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Internet :: WWW/HTTP :: Site Management
Classifier: Topic :: Text Processing :: Markup :: Markdown
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: click>=8.1
Requires-Dist: jinja2>=3.1
Requires-Dist: mistune>=3.0
Requires-Dist: pyyaml>=6.0
Requires-Dist: watchdog>=4.0
Requires-Dist: websockets>=12.0
Requires-Dist: rjsmin>=1.2
Requires-Dist: pillow>=10.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0; extra == "dev"
Dynamic: license-file

# medusa-ssg

Minimal Python static site generator. Markdown + Jinja2, Tailwind CSS built-in, zero configuration, no frontmatter.

## Quick Start

```bash
pip install medusa-ssg
medusa new mysite
cd mysite
medusa serve
```

Open http://localhost:4000 to see your site with live reload.

## Features

- **Zero frontmatter** — title, date, tags, and description derived from filenames and content
- **Markdown + Jinja2** — write content in Markdown, layouts in Jinja2
- **Tailwind CSS** — built-in pipeline with typography plugin
- **Live reload** — dev server with WebSocket-based hot reload
- **Pretty URLs** — `/posts/hello/` not `/posts/hello.html`
- **Automatic sitemap & RSS** — generated on build
- **Custom 404 pages** — static HTML served with proper status codes

## Requirements

- Python 3.10+
- Node.js 16+ (for Tailwind CSS)

## Installation

```bash
# Install Medusa
pip install medusa-ssg

# Or install from source
git clone https://github.com/yourname/medusa.git
cd medusa
pip install -e .
```

## CLI Commands

```bash
medusa new NAME          # Create a new project
medusa build             # Build site to output/
medusa build --drafts    # Include draft content
medusa serve             # Dev server at localhost:4000
medusa serve --port 3000 # Custom port
medusa serve --drafts    # Include drafts in dev
```

## Project Structure

```
mysite/
├── site/                    # Content and templates
│   ├── _layouts/            # Page layouts
│   │   └── default.html.jinja
│   ├── _partials/           # Reusable components
│   │   ├── header.html.jinja
│   │   └── footer.html.jinja
│   ├── posts/               # Blog posts
│   │   └── 2024-01-15-hello-world.md
│   ├── index.jinja          # Home page
│   ├── about.md             # Static page
│   └── 404.html             # Custom error page
├── assets/
│   ├── css/main.css         # Tailwind entry point
│   ├── js/main.js
│   └── images/
├── data/                    # YAML data files
│   ├── site.yaml            # Site metadata
│   └── nav.yaml             # Navigation links
├── output/                  # Generated site (git-ignored)
├── medusa.yaml              # Configuration
├── tailwind.config.js
└── package.json
```

## Configuration

Create `medusa.yaml` in your project root:

```yaml
# Output directory (default: output)
output_dir: output

# Dev server port (default: 4000)
port: 4000

# WebSocket port for live reload (default: port + 1)
ws_port: 4001
```

## Writing Content

### Markdown Pages

Create `.md` files anywhere in `site/`:

```markdown
# My Page Title

Content goes here. The first `# heading` becomes the page title.

Use #hashtags for tags. #python #tutorial
```

### Dated Posts

Name files with a date prefix for automatic date extraction:

```
site/posts/2024-01-15-my-post.md  →  /posts/my-post/
site/posts/2024-02-20-another.md  →  /posts/another/
```

### Custom Layouts

Specify a layout with brackets in the filename:

```
site/about[minimal].md  →  Uses _layouts/minimal.html.jinja
site/contact.md         →  Uses _layouts/default.html.jinja
```

### Drafts

Prefix files or folders with `_` to mark as draft:

```
site/posts/_work-in-progress.md   # Draft post
site/_experiments/test.md          # Draft folder
```

Drafts are excluded from builds unless you use `--drafts`.

## Templates

### Available Variables

| Variable | Description |
|----------|-------------|
| `current_page` | The current page object |
| `pages` | Collection of all pages |
| `tags` | Map of tag names to page collections |
| `data` | Merged YAML from `data/` directory |
| `url_for(path)` | Generate URLs with base path |

### Page Object

```python
current_page.title        # Page title
current_page.content      # Rendered HTML
current_page.description  # First paragraph
current_page.url          # URL path (/posts/hello/)
current_page.date         # Publication date
current_page.tags         # List of tags
current_page.draft        # Is draft?
current_page.layout       # Layout name
current_page.group        # Folder group (posts, pages, etc.)
```

### Collections API

```jinja
{# Get posts #}
{% for post in pages.group("posts").sorted() %}
  <a href="{{ post.url }}">{{ post.title }}</a>
{% endfor %}

{# Filter by tag #}
{% for post in pages.with_tag("python").latest(5) %}
  {{ post.title }}
{% endfor %}

{# Published only (excludes drafts) #}
{% for page in pages.published() %}
  {{ page.title }}
{% endfor %}
```

### Partials

```jinja
{# site/_partials/card.html.jinja #}
<div class="card">
  <h3>{{ title }}</h3>
  <p>{{ description }}</p>
</div>

{# Include in any template #}
{% include "card.html.jinja" %}
```

## Data Files

YAML files in `data/` are available in templates:

```yaml
# data/site.yaml - merged into data root
title: My Site
author: Jane Doe

# data/social.yaml - available as data.social
- platform: GitHub
  url: https://github.com/username
- platform: Twitter
  url: https://twitter.com/username
```

```jinja
<h1>{{ data.title }}</h1>
<p>By {{ data.author }}</p>

{% for link in data.social %}
  <a href="{{ link.url }}">{{ link.platform }}</a>
{% endfor %}
```

## Static Files

### 404 Page

Create `site/404.html` for a custom error page. This file is copied directly to output without layout processing and served with a 404 status code by the dev server.

### Other Static HTML

Any `.html` file (not `.html.jinja`) in `site/` is copied as-is to output, preserving the path structure.

## Deployment

### Netlify

Create `netlify.toml`:

```toml
[build]
  command = "pip install -e . && medusa build"
  publish = "output"

[[redirects]]
  from = "/*"
  to = "/404.html"
  status = 404
```

### Vercel

Create `vercel.json`:

```json
{
  "buildCommand": "pip install -e . && medusa build",
  "outputDirectory": "output"
}
```

### GitHub Pages

Create `.github/workflows/deploy.yml`:

```yaml
name: Deploy
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.12'
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: pip install medusa-ssg
      - run: npm install
      - run: medusa build
      - uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./output
```

### Manual / Any Host

```bash
medusa build
# Upload contents of output/ to your server
```

## Development

```bash
# Clone and install
git clone https://github.com/yourname/medusa.git
cd medusa
pip install -e ".[dev]"

# Run tests
pytest

# Run with coverage
pytest --cov=medusa --cov-report=term-missing
```

## License

MIT License. See [LICENSE](LICENSE) for details.
