# `Astral.Plugin`
[🔗](https://github.com/elixir-volt/astral/blob/v0.7.0/lib/astral/plugin.ex#L1)

Behaviour for Astral site plugins.

Plugins participate in site configuration, discovery, page rendering, and
build lifecycle events. All callbacks except `name/0` are optional.

Plugins may be configured as modules or `{module, opts}` tuples. When a
plugin defines a callback with one extra arity, Astral passes tuple opts as
the final argument.

Plugins can opt into Vite/Volt-style ordering with `enforce/0` or
`enforce/1`: `:pre` plugins run before normal plugins, and `:post` plugins
run after them.

# `plugin`

```elixir
@type plugin() :: module() | {module(), keyword()}
```

# `route_body`

```elixir
@type route_body() :: iodata()
```

# `route_headers`

```elixir
@type route_headers() :: [{String.t(), String.t()}]
```

# `build_done`
*optional* 

```elixir
@callback build_done(result :: Astral.BuildResult.t()) :: :ok | {:error, term()}
```

Run after a successful static build.

# `build_done`
*optional* 

```elixir
@callback build_done(result :: Astral.BuildResult.t(), opts :: keyword()) ::
  :ok | {:error, term()}
```

# `build_start`
*optional* 

```elixir
@callback build_start(config :: Astral.Config.t()) :: :ok | {:error, term()}
```

Run before a static build starts.

# `build_start`
*optional* 

```elixir
@callback build_start(config :: Astral.Config.t(), opts :: keyword()) ::
  :ok | {:error, term()}
```

# `config`
*optional* 

```elixir
@callback config(config :: Astral.Config.t()) :: Astral.Config.t()
```

Transform the normalized site config before discovery/build/dev use.

# `config`
*optional* 

```elixir
@callback config(config :: Astral.Config.t(), opts :: keyword()) :: Astral.Config.t()
```

# `enforce`
*optional* 

```elixir
@callback enforce() :: :pre | :post | nil
```

Return `:pre`, `:post`, or `nil` to control plugin ordering.

# `enforce`
*optional* 

```elixir
@callback enforce(opts :: keyword()) :: :pre | :post | nil
```

# `name`

```elixir
@callback name() :: String.t()
```

Plugin name for identification and error messages.

# `render_page`
*optional* 

```elixir
@callback render_page(
  html :: String.t(),
  page :: Astral.Page.t(),
  site :: Astral.Site.t()
) ::
  {:ok, String.t()} | {:error, term()} | nil
```

Transform rendered page HTML.

# `render_page`
*optional* 

```elixir
@callback render_page(
  html :: String.t(),
  page :: Astral.Page.t(),
  site :: Astral.Site.t(),
  opts :: keyword()
) :: {:ok, String.t()} | {:error, term()} | nil
```

# `render_route`
*optional* 

```elixir
@callback render_route(route :: Astral.Route.t(), site :: Astral.Site.t()) ::
  {:ok, route_body()}
  | {:ok, route_body(), String.t()}
  | {:ok, route_body(), String.t(), route_headers()}
  | {:error, term()}
  | nil
```

Render a plugin-owned generated route.

# `render_route`
*optional* 

```elixir
@callback render_route(
  route :: Astral.Route.t(),
  site :: Astral.Site.t(),
  opts :: keyword()
) ::
  {:ok, route_body()}
  | {:ok, route_body(), String.t()}
  | {:ok, route_body(), String.t(), route_headers()}
  | {:error, term()}
  | nil
```

# `routes`
*optional* 

```elixir
@callback routes(site :: Astral.Site.t()) :: [Astral.Route.t()]
```

Return generated routes such as feeds, sitemaps, tag pages, or pagination pages.

# `routes`
*optional* 

```elixir
@callback routes(site :: Astral.Site.t(), opts :: keyword()) :: [Astral.Route.t()]
```

# `site_discovered`
*optional* 

```elixir
@callback site_discovered(site :: Astral.Site.t()) :: Astral.Site.t()
```

Transform a discovered site before rendering.

# `site_discovered`
*optional* 

```elixir
@callback site_discovered(site :: Astral.Site.t(), opts :: keyword()) :: Astral.Site.t()
```

---

*Consult [api-reference.md](api-reference.md) for complete listing*
