Skip to content

Commit

Permalink
lots of docs
Browse files Browse the repository at this point in the history
  • Loading branch information
Waylon S. Walker committed Jan 29, 2025
1 parent 1b8d4fd commit e8114e7
Show file tree
Hide file tree
Showing 23 changed files with 1,671 additions and 25 deletions.
287 changes: 287 additions & 0 deletions .plugin-prompt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,287 @@
# Markata Plugin Development Guide

This guide will help you create plugins for the Markata static site generator.
Follow these patterns and best practices to ensure your plugin integrates
seamlessly with Markata's architecture.

## Plugin Structure

Every Markata plugin follows this standard structure:

1. **Docstring and Imports**: Begin with a comprehensive docstring followed by
required imports
2. **Plugin Identification**: Define plugin name constants
3. **Configuration Models**: Define Pydantic models for configuration
4. **Hook Implementations**: Implement lifecycle hooks with proper decorators

## Required Components

### 1. Documentation

Every plugin must start with a docstring following this format:

```python
"""
Plugin name and one-line description
# Installation
Installation instructions, including:
- Required pip packages
- Configuration in markata.toml
- Any environment variables needed
\`\`\`toml
hooks = [
"markata.plugins.load",
]
\`\`\`
# Configuration
Document all configuration options:
\`\`\`toml
[markata.plugin_name]
option1 = "default" # Description of option1
option2 = 42 # Description of option2
\`\`\`
# Usage
Explain how to use the plugin, with examples.
# Notes
Any additional information, caveats, or best practices.
"""
```

### 2. Imports and Plugin Identity

```python
from markata.hookspec import hook_impl, register_attr
import pydantic
from typing import List, Optional, Union

MARKATA_PLUGIN_NAME = "My Plugin" # Human readable name
MARKATA_PLUGIN_PACKAGE_NAME = "my-plugin" # Package/import name
```

### 3. Configuration Models

Define Pydantic models using appropriate configuration based on usage pattern:

```python
class PluginConfig(pydantic.BaseModel):
option1: str = "default"
option2: int = 42

model_config = pydantic.ConfigDict(
validate_assignment=True, # For config models
arbitrary_types_allowed=True,
extra="allow",
str_strip_whitespace=True,
validate_default=True,
coerce_numbers_to_str=True,
populate_by_name=True,
)

class Config(pydantic.BaseModel):
plugin_name: PluginConfig = PluginConfig()

@hook_impl
@register_attr("config_models")
def config_model(markata: "Markata") -> None:
markata.config_models.append(Config)
```

### 4. Hook Implementation

Implement hooks based on when your plugin needs to run in the lifecycle:

```python
@hook_impl
@register_attr("my_attribute") # Register any new markata attributes
def hook_name(markata: "Markata") -> None:
"""
Implement your plugin logic here.
Always type hint markata parameter.
Register attributes you add to markata.
"""
pass
```

## Lifecycle Hooks

Choose appropriate hooks based on when your plugin needs to run:

1. `config_model`: Load configuration models
2. `post_model`: Load post models
3. `create_models`: Model creation/merging
4. `load_config`: Load configuration
5. `configure`: Configuration setup
6. `validate_config`: Configuration validation
7. `glob`: File discovery
8. `load`: File loading
9. `validate_posts`: Post validation
10. `pre_render`: Pre-render processing
11. `render`: Content rendering
12. `post_render`: Post-render processing
13. `save`: Save to disk
14. `teardown`: Cleanup on exit

## Working with Posts

### Searching/Filtering Posts

```python
# Use markata.map for post operations
posts = markata.map(
'post', # What to return
filter='"tag" in post.tags', # Filter condition
sort='date', # Sort field
reverse=True # Sort direction
)
```

### Iterating Over All Posts

Typically plugins will iterate over all posts that are not markated as `skip`
posts, these posts have been manually flagged by the user to skip, or have been
identified early as already completed in previoud builds.

```python
for post in markata.filter('not skip'):
# Do something with post
```

### Creating Posts

```python
post_args = {
"markata": markata,
"path": "example.md",
"content": "# Example\nContent here",
"raw": "# Example\nContent here",
"title": "Example Post",
"description": "An example post",
"published": True,
"date": "2025-01-29"
}

post = markata.Post.parse_obj(post_args)
markata.Post.validate(post)
markata.posts.append(post)
```

## Complete Example

Here's a complete example of a tag statistics plugin:

```python
"""
Tag Statistics Plugin
Generates statistics about tag usage across all posts.
# Installation
\`\`\` toml
hooks = ["markata.plugins.tag_stats"]
\`\`\`
# Configuration
\`\`\` toml
[tag_stats]
output_file = "tag-stats.md" # Output file for statistics
min_count = 1 # Minimum tag count to include
\`\`\`
"""

from markata.hookspec import hook_impl, register_attr
import pydantic
from pathlib import Path
from typing import Optional
from collections import Counter

MARKATA_PLUGIN_NAME = "Tag Statistics"
MARKATA_PLUGIN_PACKAGE_NAME = "tag-stats"

class TagStatsConfig(pydantic.BaseModel):
output_file: Path = Path("tag-stats.md")
min_count: int = 1

model_config = pydantic.ConfigDict(
validate_assignment=True,
arbitrary_types_allowed=True,
extra="allow",
str_strip_whitespace=True,
validate_default=True,
coerce_numbers_to_str=True,
populate_by_name=True,
)

class Config(pydantic.BaseModel):
tag_stats: TagStatsConfig = TagStatsConfig()

@hook_impl
@register_attr("config_models")
def config_model(markata: "Markata") -> None:
markata.config_models.append(Config)

@hook_impl
def post_render(markata: "Markata") -> None:
# Collect all tags
all_tags = []
for post in markata.posts:
if hasattr(post, "tags"):
all_tags.extend(post.tags)

# Count tags
tag_counts = Counter(all_tags)

# Generate markdown
content = "# Tag Statistics\n\n"
content += "| Tag | Count |\n|-----|-------|\n"

for tag, count in sorted(tag_counts.items()):
if count >= markata.config.tag_stats.min_count:
content += f"| {tag} | {count} |\n"

# Create statistics post
post_args = {
"markata": markata,
"path": str(markata.config.tag_stats.output_file),
"content": content,
"raw": content,
"title": "Tag Statistics",
"description": "Statistics about tag usage",
"published": True,
"date": markata.today
}

post = markata.Post.parse_obj(post_args)
markata.Post.validate(post)
markata.posts.append(post)

```

## CLI Plugins

To add CLI commands:

```python
@hook_impl()
def cli(app, markata):
@app.command()
def my_command():
"""Command description for --help"""
# Command implementation
```

> note throughout this file nested markdown fences were escaped for
> compatibility with the prompt, do not escape them. for example, you should
> not use \`\`\` toml but rather ``` toml
293 changes: 293 additions & 0 deletions 404.html

Large diffs are not rendered by default.

16 changes: 1 addition & 15 deletions docs/admonitions.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,75 +4,61 @@ description: This is what the default admonition styles look like and how to cre
---

???+ note open by default
you can open a details tab with '+'
you can open a details tab with '+'

<!-- prettier-ignore -->
??? note closed by default
you can open a details tab with '+'

## all of the admonitions

<!-- prettier-ignore -->
!!! note
a note

<!-- prettier-ignore -->
!!! abstract
an abstract

<!-- prettier-ignore -->
!!! info

admonitions

<!-- prettier-ignore -->
!!! tip

You should think about using admonitions

<!-- prettier-ignore -->
!!! success

Run Successful!

<!-- prettier-ignore -->
!!! question

What do you think of this?

<!-- prettier-ignore -->
!!! source
Add some source code.
```python
print('hello world')

````
<!-- prettier-ignore -->
!!! warning
a warning
<!-- prettier-ignore -->
!!! failure
a failure
<!-- prettier-ignore -->
!!! danger
some danger
<!-- prettier-ignore -->
!!! bug
a bug
<!-- prettier-ignore -->
!!! example
an example
``` python
print('hello world')
```
<!-- prettier-ignore -->
!!! quote
a quote
Expand Down
5 changes: 2 additions & 3 deletions markata.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
'plugins' = 'https://markata.dev/plugins/'

[markata.style]
body_width = "1200px"

[markata]
# bump site version to bust GitHub actions cache
Expand All @@ -34,7 +33,7 @@ default_cache_expire = 1209600
# subroute = "docs"

## Markata Setup
glob_patterns = "docs/**/*.md,CHANGELOG.md"
glob_patterns = ["docs/**/*.md", "CHANGELOG.md"]
output_dir = "markout"
assets_dir = "static"
hooks = [
Expand Down Expand Up @@ -295,5 +294,5 @@ plugin = "markata.plugins.md_it_wikilinks:wikilinks_plugin"
config = { markata = "markata" }

[markata.glob]
glob_patterns = "docs/**/*.md,CHANGELOG.md"
glob_patterns = ["docs/**/*.md","CHANGELOG.md"]
use_gitignore = true
1 change: 1 addition & 0 deletions markata/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
]

DEFAULT_HOOKS = [
"markata.plugins.didyoumean",
"markata.plugins.skip",
"markata.plugins.md_it_wikilinks",
"markata.plugins.copy_assets",
Expand Down
Loading

0 comments on commit e8114e7

Please sign in to comment.