npm package: @karaoke-cms/module-seo
Description: SEO module for karaoke-cms — OG images, JSON-LD, robots.txt
Latest version: 0.20.10

# @karaoke-cms/module-seo

Zero-config SEO for karaoke-cms — OG social card images, JSON-LD structured data, canonical tags, Twitter meta, `robots.txt` (with `Sitemap:` when `site` is set), and `llms.txt` for LLM-oriented discovery. Add it once; every published page gets the full treatment automatically.

## Installation

```bash
npm install @karaoke-cms/module-seo
```

## Usage

```ts
// karaoke.config.ts
import { defineConfig } from '@karaoke-cms/astro';
import { seo } from '@karaoke-cms/module-seo';

export default defineConfig({
  // ...
  modules: [seo()],
});
```

No options required. The module reads your existing `title`, `description`, `date`, and `author` frontmatter fields automatically.

## What it adds

### OG social card images

At build time, the module scans your `dist/` folder and generates a `.png` social card for every page that has an `og:title` meta tag. Images are written to `dist/og/<path>.png` and wired up as `og:image` automatically.

Cards are rendered with [Satori](https://github.com/vercel/satori) using a bundled Inter font — no external services, no API keys.

### JSON-LD structured data

Every blog post gets a `BlogPosting` schema. Every docs page gets a `TechArticle` schema. Fields populated: `name`, `description`, `datePublished`, `author`.

### Head meta tags

The module injects (or fixes) the following on every page:

| Tag | Value |
|-----|-------|
| `<title>` | De-duplicated (no `My Site — My Site`) |
| `canonical` | Full URL from `site` in `astro.config.mjs` |
| `og:title` | Post/doc title |
| `og:description` | Frontmatter `description` |
| `og:site_name` | Site `title` from `karaoke.config.ts` |
| `og:locale` | `en_US` |
| `twitter:card` | `summary_large_image` |
| `twitter:title` | Post/doc title |
| `twitter:description` | Frontmatter `description` |

### robots.txt

Writes `/robots.txt` on build, allowing all crawlers and disallowing `/karaoke-cms/` (the dev-only handbook). When `site` is set, adds a **`Sitemap:`** line if the Astro sitemap integration emitted `sitemap-index.xml` or `sitemap.xml` in `dist/`.

### llms.txt

Writes `/llms.txt` on build when `site` is set — a small index (home, sitemap, RSS, optional JSON index link) for tools that look for it. Opt out with `seo({ llmsTxt: false })`. Optional `llmsSiteTitle` and `llmsTxtExtra` let you customize the heading and append Markdown.

### karaoke-public-content.json

Writes a metadata manifest of public pages (`url`, `title`, optional `description`, `datePublished`, `author`, `ogType`) by scanning built HTML for Open Graph and article meta. Same trust boundary as the static site. Opt out with `seo({ contentManifest: false })`.

## Routes

| Pattern | Description |
|---------|-------------|
| `/robots.txt` | Allows all crawlers, optional `Sitemap:` line, disallows `/karaoke-cms/` |
| `/llms.txt` | Optional LLM-oriented index (when `site` is set and `llmsTxt` is not `false`) |
| `/karaoke-public-content.json` | Optional page metadata index (when `site` is set and `contentManifest` is not `false`) |

## Requirements

Set `site:` in `astro.config.mjs` to your production URL for canonical tags and OG images to resolve correctly:

```js
// astro.config.mjs
export default defineConfig({
  site: 'https://your-domain.com',
  // ...
});
```

## What's new in 0.18.2

- **`rel=canonical` injection from `og:url`** -- after generating OG images, the module now scans all built HTML files. Any page that has an `og:url` meta tag but no existing `<link rel="canonical">` gets one injected automatically, matching the `og:url` value. This ensures search engines see a canonical URL on every page without requiring theme authors to add the tag manually.
- **Dead `robots.txt` page removed** -- the static `robots.txt.ts` Astro page file has been deleted. `robots.txt` is now written directly to `dist/` during the `astro:build:done` hook, which is simpler and avoids route injection.

## What's new in 0.13.1

No user-facing changes in this release.

## What's new in 0.11.3

No user-facing changes in this release.

## What's new in 0.11.0

- **Initial release** — OG image generation, JSON-LD structured data, `robots.txt`
- **Title deduplication** — fixes `My Site — My Site` in `<title>` tags
- **Canonical URLs** — `<link rel="canonical">` on every page
- **Rich meta** — `og:site_name`, `og:locale`, Twitter card tags
- **Rich JSON-LD** — `datePublished` and `author` fields on article schemas