An open API service indexing awesome lists of open source software.

https://github.com/sitapix/mkdocs-material-to-starlight

Convert MkDocs Material sites to Astro Starlight quickly
https://github.com/sitapix/mkdocs-material-to-starlight

astro astro-starlight docs-as-code documentation-generator markdown material-for-mkdocs mdx migration mkdocs mkdocs-material starlight static-site-generator

Last synced: 1 day ago
JSON representation

Convert MkDocs Material sites to Astro Starlight quickly

Awesome Lists containing this project

README

          

# mkdocs-material-to-starlight

[![CI](https://github.com/sitapix/mkdocs-material-to-starlight/actions/workflows/ci.yml/badge.svg)](https://github.com/sitapix/mkdocs-material-to-starlight/actions/workflows/ci.yml)
[![npm version](https://img.shields.io/npm/v/mkdocs-material-to-starlight.svg)](https://www.npmjs.com/package/mkdocs-material-to-starlight)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
[![Node](https://img.shields.io/node/v/mkdocs-material-to-starlight.svg)](./package.json)

**Move your MkDocs Material site to Astro Starlight without rewriting pages by hand.**

Point one command at your `mkdocs.yml` and get a buildable Starlight project: pages converted, plugins mapped, sidebar wired up, redirects preserved, i18n intact. Anything the converter cannot handle lands in `MIGRATION_NOTES.md` with a file and line number.

---

## Quick start

```bash
npx mkdocs-material-to-starlight
```

The interactive wizard reads your `mkdocs.yml`, asks about the decisions that apply to your site, and writes a working Astro project. Then:

```bash
cd ./starlight-out
npm install
npm run dev
```

Your docs are live on Starlight. Requires Node 20+.

> **Preview the plan without writing files:**
> `npx mkdocs-material-to-starlight ./my-mkdocs --explain`

---

## Why use this

- **Tested at real scale.** A first `--check` run typically takes 1–5 minutes; `astro check` is the slow step, not the converter. Repeat `--check` runs finish in 10–30s. Without `--check`, conversion alone finishes in seconds.
- **Maps every Material feature.** Admonitions, tabs, grids, snippets, icons, math, mermaid, i18n, mike versions. Features without a clean Starlight equivalent (Jinja macros, custom theme overrides) become diagnostics with file and line numbers.
- **Scripts cleanly.** The wizard prints its equivalent unattended command on exit. Drop that command into a CI workflow. Exit codes follow Unix convention.
- **Idempotent.** Running it twice produces byte-identical output, so reruns do not churn diffs.

---

## What it converts

If MkDocs Material renders it, this tool maps it. The mapping by area:

Markdown syntax and PyMdown extensions

| MkDocs Material | Starlight output |
|---|---|
| `!!! note "Title"` admonitions (12 types) | `:::note[Title]` aside directives, type-mapped to Starlight's 4 |
| `??? note` / `???+ note` collapsible | `Title...` |
| `=== "Tab"` content tabs | `

` (with shim CSS) |
| `
` | `
` |
| `
` | `
` |
| `:material-rocket:` / `:fontawesome-brands-github:` | `:icon[rocket]` / `:icon[github]`, with curated name mapping plus SVG fallback |
| `--8<-- "snippet.md"` | Inlines snippet content (with cycle detection and depth limit) |
| `==text==` highlights | `text` |
| `H~2~O` subscripts and `2^10^` superscripts | `` and `` |
| `++ctrl+alt+del++` keyboard keys | `Ctrl+Alt+Del` |
| `[link](api/auth.md)` internal refs | Rewritten to Starlight slugs (`[link](/api/auth)`) |
| Footnotes (`[^1]`) | GFM footnotes via remark-gfm |
| Math (`$inline$`, `$$block$$`) | remark-math plus rehype-katex (deps included) |
| ` ```mermaid ` blocks | astro-mermaid (dep included) |
| MagicLink autolinks (`@user`, `#123`) | Markdown links pointing at GitHub from `repo_url` |
| Definition lists, abbreviations, buttons, CriticMarkup, code annotations | Normalized to standard Markdown or styled HTML |

Site config and navigation

| MkDocs | Starlight output |
|---|---|
| `nav:` tree | `sidebar` config in `astro.config.mjs` |
| `site_name`, `site_description`, `site_url` | `title`, `description` on the integration; `site` on Astro config |
| Missing frontmatter `title` | Synthesized from first H1 or humanized filename (Starlight requires it) |

Plugins

| MkDocs plugin | Starlight output |
|---|---|
| `mkdocs-redirects` | `redirects: { … }` in `astro.config.mjs` |
| `mkdocs-static-i18n` | Directory-prefix layout (`fr/page.md`) plus `locales: { … }` |
| `mkdocs-section-index` | Section `index.md` hoisted to first child of its sidebar group |
| `mkdocs-literate-nav` | `SUMMARY.md` parsed and used as the nav source |
| `mkdocs-include-markdown-plugin` | `{% include %}` resolved inline before conversion |
| `mkdocs-rss-plugin` | `@astrojs/rss` dep plus `src/pages/rss.xml.ts` scaffold |
| `mkdocs-glightbox` | `starlight-image-zoom` dep |
| `mike` (versioned docs) | `starlight-versions` dep |
| `mkdocs-git-revision-date-localized` | Built-in `lastUpdated: true` |
| `blog`, `tags` (Material) | `starlight-blog`, `starlight-tags` deps |
| `mkdocs-macros-plugin` (Jinja2) | Per-occurrence diagnostic with file:line locator (cannot be evaluated) |
| `gen-files`, `print-site`, `monorepo`, `multirepo`, `social`, `meta`, `privacy`, `mkdocstrings`, `mkdocs-jupyter` | Diagnostic in `MIGRATION_NOTES.md` with documented workaround |

---

## What you get

```
output/
├── astro.config.mjs ← migrated config: sidebar, redirects, locales
├── package.json ← scripts and pinned deps for every feature you used
├── MIGRATION_NOTES.md ← human-readable diagnostics, grouped by rule
├── public/ ← non-Markdown assets (images, PDFs) copied through
└── src/
├── content/docs/ ← every Markdown page, converted
└── styles/mkdocs-migration.css ← shim so grids, cards, and tabs render correctly
```

The project builds as-is for the common case. `cd output && npm install && npm run dev` and you have a running Starlight site.

---

## Common workflows

```bash
# First-time conversion: interactive wizard (recommended)
npx mkdocs-material-to-starlight

# Unattended (CI or scripted): accepts the wizard's defaults
npx mkdocs-material-to-starlight ./mkdocs-project ./starlight-out --yes

# Dry-run: print the migration plan, write nothing
npx mkdocs-material-to-starlight ./mkdocs-project --explain

# Run with astro check so type and link errors fail fast
npx mkdocs-material-to-starlight ./mkdocs-project ./starlight-out --yes --check

# Resolve PyMdown snippets from a custom directory
npx mkdocs-material-to-starlight ./mkdocs-project ./starlight-out \
--yes --snippet-base-path docs --snippet-base-path includes
```

The wizard prints the equivalent unattended command when it finishes, ready to paste into CI.

---

## Diagnostics

The converter does not throw on bad input. Anything it cannot handle becomes a typed diagnostic on the run report. A malformed admonition will not abort a 2,000-page conversion.

In your terminal:

```
api/auth.md:12:4 warning broken-link link target "missing.md" was not found in the slug map
```

In `outputDir/MIGRATION_NOTES.md`:

- A per-rule breakdown of every diagnostic, grouped by file
- Any unmapped `mkdocs.yml` top-level fields you may want to migrate by hand
- Workaround pointers for plugins that have no clean Starlight equivalent

Every rule is documented. `--explain` prints the registered description and fix for each one before you run a conversion.

---

## CLI reference

```
mkdocs-material-to-starlight [options]
mkdocs-material-to-starlight --explain
mkdocs-material-to-starlight compare [options]

Convert options:
--snippet-base-path Resolve PyMdown snippets against this directory.
Repeatable; first match wins.
--check After conversion, run `astro check` against the
output and surface its diagnostics.
--check-timeout Override the astro-check timeout (default: 5min).
--dry-run Plan only, do not write files. (Not yet wired through.)
--yes Accept wizard defaults; skip interactive prompts.

Compare options (visual diff between rendered MkDocs and Starlight pages):
--pages a,b,c Comma-separated paths to diff (default: /).
--threshold 0.01 Mismatch ratio that still counts as a match.
--report file.md Write the Markdown report to a file instead of stdout.

Common:
-h, --help Show help.
--version Print the version.
```

Exit codes: `0` success, `1` runtime or check failure, `2` usage error.

The `compare` subcommand requires Playwright and pixelmatch as optional peers:

```bash
npm install playwright pixelmatch pngjs
npx playwright install chromium
```

These are optional. The converter itself does not depend on them.

---

## Programmatic API

```ts
import { convertSiteFromDisk } from 'mkdocs-material-to-starlight';

const result = await convertSiteFromDisk({
projectDir: '/path/to/mkdocs-project',
outputDir: '/path/to/output',
snippetBasePaths: ['docs'], // optional; enables snippet expansion
});

if (!result.ok) {
console.error(`${result.error.code}: ${result.error.message}`);
process.exit(1);
}

for (const tagged of result.value.diagnostics) {
console.log(`${tagged.sourcePath}: ${tagged.diagnostic.ruleId}: ${tagged.diagnostic.message}`);
}
```

The success result also exposes `astroConfigSource`, `packageJsonSource`, `migrationNotesSource`, and `sidebarSource` for inspection or custom write strategies.

---

## Limitations

Read these before you commit the output:

- **Theme palette and custom CSS or JS** (`theme.palette`, `overrides/`, `extra_css`, `extra_javascript`) land in `MIGRATION_NOTES.md` rather than getting auto-translated. Starlight's design system has a different structure from Material's, so you will want to re-pick colors against the Starlight theme.
- **`mkdocs-macros-plugin` Jinja2 expressions** cannot be evaluated. Each `{{ … }}` and `{% … %}` site is reported with file and line so it can be replaced by hand.
- **`mkdocs-section-index` and `mkdocs-literate-nav`** cover the common cases. Advanced patterns (per-directory recursive `SUMMARY.md`, implicit-index injection for entries not in `nav:`) are not yet implemented.
- **`--dry-run`** is parsed but a no-op. Use `--explain` instead.

Run `--explain` first to see which features in your site will trigger diagnostics.

---

## How it works

Built on the [unified](https://unifiedjs.com) and [remark](https://github.com/remarkjs/remark) ecosystem. Four design pillars:

- **Plugin-isolated.** Every transform owns a disjoint MDAST `(node-type, name)` namespace. Plugins are commutative; reordering them does not change output.
- **Idempotent.** `convert(convert(x)) === convert(x)` byte-equal. Verified at unit, composed, file, site, and CLI levels.
- **Diagnostic-first.** Failures attach typed diagnostics to the report. They never throw.
- **Functional core, imperative shell.** Pure logic in `domain/` and `use-cases/`. All I/O lives behind ports in `infrastructure/`.

```
src/
├── domain/ Pure types, value objects, ports (no I/O, no framework deps)
├── use-cases/ Application orchestration; functional core
├── infrastructure/ Adapters for file system, YAML, unified; the imperative shell
└── interface/ CLI and programmatic API; the only place that wires concrete adapters
```

The full working agreement and architectural rules live in [`CLAUDE.md`](./CLAUDE.md).

---

## Development

Requires Node 20+.

```bash
npm install
npm test # full suite, runs in ~10s
npm run typecheck # tsc --noEmit
npm run build # emit dist/

npx vitest run path/to/file.test.ts # single test file
npx vitest run -t 'pattern matches subject' # single test by title
```

Every commit that introduces production code includes the failing test that motivated it. The idempotency property test runs the full pipeline twice on every fixture and asserts byte-equality of the second pass.

Bug reports, real-world fixtures, and PRs are welcome at [github.com/sitapix/mkdocs-material-to-starlight/issues](https://github.com/sitapix/mkdocs-material-to-starlight/issues). Sites that break the converter are the most valuable contribution.

---

## License

[MIT](./LICENSE) © sitapix