blume 1.0.3 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +94 -0
- package/dist/cli/index.js +13784 -10579
- package/dist/cli/index.js.map +93 -61
- package/dist/types/core/config-input.d.ts +87 -8
- package/dist/types/core/data.d.ts +21 -0
- package/dist/types/core/deployment-env.d.ts +6 -0
- package/dist/types/core/diagnostics.d.ts +23 -0
- package/dist/types/core/i18n-ui.d.ts +140 -140
- package/dist/types/core/schema.d.ts +549 -370
- package/dist/types/core/sources/types.d.ts +3 -1
- package/dist/types/core/standard-schema.d.ts +41 -0
- package/dist/types/core/types.d.ts +23 -0
- package/dist/types/og/card.d.ts +63 -0
- package/dist/types/og/dimensions.d.ts +12 -0
- package/dist/types/openapi/references.d.ts +12 -7
- package/docs/01-quickstart.mdx +1 -1
- package/docs/02-deployment.mdx +9 -1
- package/docs/advanced/api-reference.mdx +22 -3
- package/docs/advanced/changelog.mdx +1 -1
- package/docs/advanced/skills.mdx +1 -1
- package/docs/configuration/ai.mdx +1 -1
- package/docs/configuration/customization.mdx +1 -1
- package/docs/configuration/export.mdx +1 -1
- package/docs/configuration/index.mdx +21 -1
- package/docs/configuration/search.mdx +28 -1
- package/docs/configuration/seo.mdx +40 -2
- package/docs/configuration/theming.mdx +1 -1
- package/docs/content/components.mdx +15 -2
- package/docs/content/index.mdx +1 -1
- package/docs/content/meta.mdx +1 -1
- package/docs/content/navigation.mdx +11 -1
- package/docs/content/sources.mdx +1 -1
- package/docs/content/syntax.mdx +116 -4
- package/docs/reference/cli.mdx +79 -1
- package/docs/reference/frontmatter.mdx +29 -1
- package/package.json +3 -3
- package/skills/blume-migrate/SKILL.md +170 -0
- package/skills/blume-migrate/assets/oxfmt@0.55.0.patch +20 -0
- package/skills/blume-migrate/references/docusaurus.md +95 -0
- package/skills/blume-migrate/references/fumadocs.md +95 -0
- package/skills/blume-migrate/references/mintlify.md +156 -0
- package/skills/blume-migrate/references/monorepo.md +224 -0
- package/skills/blume-migrate/references/nextra.md +76 -0
- package/skills/blume-migrate/references/starlight.md +116 -0
- package/skills/blume-migrate/scripts/mintlify-codemod.mjs +478 -0
- package/src/ai/llms.ts +15 -0
- package/src/astro/adapter-root.ts +70 -0
- package/src/astro/component-slots.ts +3 -2
- package/src/astro/generate.ts +132 -42
- package/src/astro/index.ts +1 -0
- package/src/astro/pages.ts +18 -3
- package/src/astro/templates.ts +158 -56
- package/src/audit/agent.ts +114 -0
- package/src/audit/catalog.ts +826 -0
- package/src/audit/checks/assets.ts +177 -0
- package/src/audit/checks/content.ts +231 -0
- package/src/audit/checks/duplicates.ts +131 -0
- package/src/audit/checks/i18n.ts +246 -0
- package/src/audit/checks/indexability.ts +213 -0
- package/src/audit/checks/links.ts +223 -0
- package/src/audit/checks/llms.ts +135 -0
- package/src/audit/checks/network.ts +272 -0
- package/src/audit/checks/og-image.ts +113 -0
- package/src/audit/checks/redirects.ts +87 -0
- package/src/audit/checks/robots.ts +114 -0
- package/src/audit/checks/sitemap.ts +229 -0
- package/src/audit/checks/social.ts +238 -0
- package/src/audit/crawl.ts +259 -0
- package/src/audit/graph.ts +74 -0
- package/src/audit/html.ts +54 -0
- package/src/audit/image-size.ts +63 -0
- package/src/audit/locate.ts +33 -0
- package/src/audit/redirects.ts +74 -0
- package/src/audit/report.ts +278 -0
- package/src/audit/run.ts +198 -0
- package/src/audit/snapshot.ts +189 -0
- package/src/audit/types.ts +214 -0
- package/src/audit/url.ts +103 -0
- package/src/cli/commands/audit.ts +205 -0
- package/src/cli/commands/build.ts +51 -12
- package/src/cli/index.ts +2 -0
- package/src/components/content/Callout.astro +8 -2
- package/src/components/content/Prompt.astro +25 -13
- package/src/components/content/Tabs.astro +98 -15
- package/src/components/layout/Breadcrumbs.astro +1 -1
- package/src/components/layout/Header.astro +5 -8
- package/src/components/layout/Logo.astro +13 -1
- package/src/components/layout/PageFeedback.astro +2 -2
- package/src/components/layout/PageLayout.astro +9 -9
- package/src/components/layout/Pagination.astro +7 -7
- package/src/components/layout/RootLayout.astro +9 -11
- package/src/components/layout/Search.astro +36 -7
- package/src/components/layout/TableOfContents.astro +1 -1
- package/src/components/layout/nav-utils.ts +9 -7
- package/src/components/openapi/Authorization.astro +80 -0
- package/src/components/openapi/Operation.astro +19 -1
- package/src/components/openapi/ParametersTable.astro +1 -1
- package/src/components/openapi/security.ts +201 -0
- package/src/components/openapi/snippets.ts +42 -13
- package/src/core/config-input.ts +94 -8
- package/src/core/data.ts +18 -2
- package/src/core/deployment-env.ts +9 -0
- package/src/core/diagnostics.ts +59 -12
- package/src/core/links.ts +2 -91
- package/src/core/nav-diagnostics.ts +48 -4
- package/src/core/navigation.ts +55 -13
- package/src/core/probe.ts +136 -0
- package/src/core/project-graph.ts +8 -0
- package/src/core/schema.ts +100 -1
- package/src/core/sources/normalize.ts +198 -25
- package/src/core/sources/types.ts +3 -1
- package/src/core/sources/watch.ts +5 -0
- package/src/core/standard-schema.ts +54 -0
- package/src/core/types.ts +23 -0
- package/src/deploy/adapter-output.ts +27 -15
- package/src/deploy/headers.ts +66 -0
- package/src/deploy/redirects.ts +49 -9
- package/src/markdown/index.ts +2 -0
- package/src/markdown/language-icon.ts +2 -1
- package/src/markdown/table-wrap.ts +43 -0
- package/src/og/card.ts +128 -36
- package/src/og/index.ts +1 -1
- package/src/og/logo.ts +21 -0
- package/src/openapi/references.ts +19 -16
- package/src/search/popular.ts +33 -0
- package/src/theme/entry.ts +56 -6
package/docs/content/syntax.mdx
CHANGED
|
@@ -275,25 +275,137 @@ npm i blume
|
|
|
275
275
|
|
|
276
276
|
## Diagrams
|
|
277
277
|
|
|
278
|
-
A `mermaid` block renders a [Mermaid](https://mermaid.js.org) diagram
|
|
278
|
+
A `mermaid` block renders a [Mermaid](https://mermaid.js.org) diagram straight from text. The fence contents are passed to Mermaid verbatim, so every diagram type Mermaid supports works here. Diagrams follow the active color theme and re-render when it changes. Author one by fencing the source with `mermaid`:
|
|
279
279
|
|
|
280
|
+
````md
|
|
280
281
|
```mermaid
|
|
281
282
|
flowchart LR
|
|
282
283
|
A[Markdown] --> B{blume build}
|
|
283
284
|
B --> C[Static HTML]
|
|
284
285
|
B --> D[llms.txt]
|
|
285
286
|
```
|
|
287
|
+
````
|
|
288
|
+
|
|
289
|
+
Diagrams render on the client, so this is an MDX-only feature, and the Mermaid library loads only on pages that include one. The rest of this section is a gallery of common types — see the [Mermaid docs](https://mermaid.js.org/intro/) for the full list.
|
|
290
|
+
|
|
291
|
+
### Flowchart
|
|
286
292
|
|
|
287
|
-
````md
|
|
288
293
|
```mermaid
|
|
289
294
|
flowchart LR
|
|
290
295
|
A[Markdown] --> B{blume build}
|
|
291
296
|
B --> C[Static HTML]
|
|
292
297
|
B --> D[llms.txt]
|
|
293
298
|
```
|
|
294
|
-
````
|
|
295
299
|
|
|
296
|
-
|
|
300
|
+
### Sequence diagram
|
|
301
|
+
|
|
302
|
+
```mermaid
|
|
303
|
+
sequenceDiagram
|
|
304
|
+
participant R as Reader
|
|
305
|
+
participant B as Blume
|
|
306
|
+
R->>B: Request /docs
|
|
307
|
+
B-->>R: Prerendered HTML
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Class diagram
|
|
311
|
+
|
|
312
|
+
```mermaid
|
|
313
|
+
classDiagram
|
|
314
|
+
class Page {
|
|
315
|
+
+string title
|
|
316
|
+
+string route
|
|
317
|
+
+render()
|
|
318
|
+
}
|
|
319
|
+
Page <|-- Doc
|
|
320
|
+
Page <|-- Changelog
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### State diagram
|
|
324
|
+
|
|
325
|
+
```mermaid
|
|
326
|
+
stateDiagram-v2
|
|
327
|
+
[*] --> Draft
|
|
328
|
+
Draft --> Published: build
|
|
329
|
+
Published --> [*]
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### Entity relationship
|
|
333
|
+
|
|
334
|
+
```mermaid
|
|
335
|
+
erDiagram
|
|
336
|
+
PAGE ||--o{ HEADING : contains
|
|
337
|
+
PAGE {
|
|
338
|
+
string title
|
|
339
|
+
string route
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### User journey
|
|
344
|
+
|
|
345
|
+
```mermaid
|
|
346
|
+
journey
|
|
347
|
+
title Publishing a page
|
|
348
|
+
section Write
|
|
349
|
+
Draft MDX: 5: Author
|
|
350
|
+
section Ship
|
|
351
|
+
blume build: 4: Author
|
|
352
|
+
Deploy: 3: Author
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### Gantt
|
|
356
|
+
|
|
357
|
+
```mermaid
|
|
358
|
+
gantt
|
|
359
|
+
title Release plan
|
|
360
|
+
dateFormat YYYY-MM-DD
|
|
361
|
+
section Docs
|
|
362
|
+
Draft :a1, 2026-01-01, 7d
|
|
363
|
+
Review :after a1, 3d
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### Git graph
|
|
367
|
+
|
|
368
|
+
```mermaid
|
|
369
|
+
gitGraph
|
|
370
|
+
commit
|
|
371
|
+
branch develop
|
|
372
|
+
checkout develop
|
|
373
|
+
commit
|
|
374
|
+
checkout main
|
|
375
|
+
merge develop
|
|
376
|
+
commit
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### Pie chart
|
|
380
|
+
|
|
381
|
+
```mermaid
|
|
382
|
+
pie title Content types
|
|
383
|
+
"Docs" : 70
|
|
384
|
+
"Blog" : 20
|
|
385
|
+
"Changelog" : 10
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### Mindmap
|
|
389
|
+
|
|
390
|
+
```mermaid
|
|
391
|
+
mindmap
|
|
392
|
+
root((Blume))
|
|
393
|
+
Content
|
|
394
|
+
MDX
|
|
395
|
+
Markdown
|
|
396
|
+
Build
|
|
397
|
+
Static HTML
|
|
398
|
+
llms.txt
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
### Timeline
|
|
402
|
+
|
|
403
|
+
```mermaid
|
|
404
|
+
timeline
|
|
405
|
+
title Blume milestones
|
|
406
|
+
2025 : Prototype
|
|
407
|
+
2026 : 1.0 release
|
|
408
|
+
```
|
|
297
409
|
|
|
298
410
|
## Callouts
|
|
299
411
|
|
package/docs/reference/cli.mdx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: CLI
|
|
3
|
-
description: Every Blume command and flag — init, dev, build, preview, add, sync, and eject.
|
|
3
|
+
description: Every Blume command and flag explained in one place — init, dev, build, preview, add, sync, and eject — along with the options each one accepts.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
```bash
|
|
@@ -21,6 +21,7 @@ blume <command> [options]
|
|
|
21
21
|
| `blume check` | Type-check the site with `astro check`. |
|
|
22
22
|
| `blume doctor` | Diagnose config and content problems. |
|
|
23
23
|
| `blume validate` | Validate links across your content. |
|
|
24
|
+
| `blume audit` | Audit the built site for SEO and health issues. |
|
|
24
25
|
|
|
25
26
|
## Common flags
|
|
26
27
|
|
|
@@ -49,6 +50,14 @@ blume <command> [options]
|
|
|
49
50
|
- `blume validate --external` — also check external links over the network.
|
|
50
51
|
- `blume validate --strict` — exit non-zero on warnings too.
|
|
51
52
|
- `blume validate --json` / `blume doctor --json` — emit diagnostics as JSON on stdout (with `code`, `severity`, `file`, `line`/`column`, and `docsUrl`) for CI and editor integrations.
|
|
53
|
+
- `blume audit --fail-on error|warning|info` — the CI gate; defaults to `error`. `--strict` is an alias for `--fail-on warning`.
|
|
54
|
+
- `blume audit --url <origin>` — also probe a live deployment for status codes, response headers, and redirect chains.
|
|
55
|
+
- `blume audit --external` — probe outbound links over the network.
|
|
56
|
+
- `blume audit --only <check|category>` / `--skip <check|category>` — narrow the report while you work through it (comma-separated).
|
|
57
|
+
- `blume audit --list-checks` — print every check the audit can report.
|
|
58
|
+
- `blume audit --verbose` — list every affected page instead of the first few.
|
|
59
|
+
- `blume audit --json` — emit the report as JSON on stdout.
|
|
60
|
+
- `blume audit --claude` / `--codex` — hand the findings to Claude Code or Codex to fix interactively.
|
|
52
61
|
|
|
53
62
|
## Verifying while the dev server runs
|
|
54
63
|
|
|
@@ -107,3 +116,72 @@ Without a project `tsconfig.json`, only the generated runtime is checked.
|
|
|
107
116
|
- **Anchor links** (`#section`, `/guides/intro#setup`) must match a heading on the target page — misses are warnings.
|
|
108
117
|
- **Asset links** (`/logo.png`) are checked against the `public/` directory.
|
|
109
118
|
- **External links** are only checked with `--external` (off by default since it requires the network); dead links (404/410/unreachable) are errors, while rate-limited or transient responses (403/429/5xx/timeout) are warnings.
|
|
119
|
+
|
|
120
|
+
## Auditing the built site
|
|
121
|
+
|
|
122
|
+
`blume validate` reads your _content_; `blume audit` reads the _built site_. It crawls the HTML in `dist/` after a build and reports SEO and site-health issues — titles, meta descriptions, canonicals, Open Graph and X cards, headings, hreflang, images, the sitemap, `robots.txt`, and structured data.
|
|
123
|
+
|
|
124
|
+
Because Blume built the site, every finding names the source file **and the front matter line** that fixes it, not just the URL a crawler would see:
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
⚠ Meta description too long or too short 5 pages
|
|
128
|
+
/docs/configuration/export content/docs/configuration/export.mdx:3
|
|
129
|
+
fix: Rewrite `description` in the frontmatter to fit the length range.
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Run it after a build:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
blume build
|
|
136
|
+
blume audit
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Findings are grouped by check rather than listed per page, so the report reads as a to-do list. Use `--verbose` to expand every affected page, and `--only`/`--skip` to work through one category at a time. `blume audit --list-checks` prints the full catalog.
|
|
140
|
+
|
|
141
|
+
### Failing CI
|
|
142
|
+
|
|
143
|
+
The exit code is the contract. By default `blume audit` fails only on errors — things that are definitely broken, like a link to a page that was never built, a redirect loop, or an invalid sitemap. Advisory findings (a short description, a duplicate title) are warnings and do not fail the build:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
blume audit # fails on errors
|
|
147
|
+
blume audit --fail-on warning # also fails on warnings
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Checking a live deployment
|
|
151
|
+
|
|
152
|
+
Some things only the real server can tell you: whether a page that exists in `dist/` actually 404s behind a bad rewrite, whether responses are compressed, and whether an `X-Robots-Tag` header is quietly deindexing a page whose HTML looks perfectly fine. Point the audit at a deployment to add those checks:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
blume audit --url https://docs.example.com
|
|
156
|
+
blume audit --url https://docs.example.com --external # also probe outbound links
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Outbound links are graded rather than flatly failed: a 404 is a broken link you can fix, while a 403 or 5xx is usually rate limiting or someone else's outage and is reported as a warning.
|
|
160
|
+
|
|
161
|
+
### Fixing the findings with an agent
|
|
162
|
+
|
|
163
|
+
If you use [Claude Code](https://claude.com/claude-code) or [Codex](https://developers.openai.com/codex/cli), the audit can hand its findings straight to it:
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
blume audit --claude # or --codex
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
This writes the complete JSON report — every affected page, not the terminal's three-page preview — to a file and opens the agent interactively with a prompt that walks it through the findings: edit the source file each finding names, apply its suggested fix, then run `blume build` and `blume audit` again until the report is clean. The session is interactive by design: you review the edits through the agent's own permission flow, and the agent is told never to fix a finding by deleting content.
|
|
170
|
+
|
|
171
|
+
`--only` and `--skip` narrow the handoff the same way they narrow the report, so you can send one category at a time.
|
|
172
|
+
|
|
173
|
+
### What it does and doesn't check
|
|
174
|
+
|
|
175
|
+
The check set is deliberately narrower than a general-purpose SEO crawler's. Much of what such a crawler reports cannot happen to a Blume site — it never emits `rel=nofollow`, and Vite's content-hashed bundles are never missing or redirecting — and reporting those as permanent zeroes would just teach you to ignore the report.
|
|
176
|
+
|
|
177
|
+
Two limits worth stating plainly:
|
|
178
|
+
|
|
179
|
+
- **Structured data** is validated for well-formedness (valid JSON, a `@context`, a `@type` on every node). Blume does not validate against the full schema.org vocabulary or Google's rich-results rules.
|
|
180
|
+
- **Core Web Vitals** are not checked. They need a real browser, and a flag that quietly measured nothing would be worse than not having one — so `blume audit` reports the layout-shift causes it _can_ see offline (images with no `width`/`height`, oversized assets) and leaves the rest alone for now.
|
|
181
|
+
|
|
182
|
+
Anything the audit did not run is reported as skipped rather than silently passing:
|
|
183
|
+
|
|
184
|
+
```
|
|
185
|
+
⊘ network skipped — pass --url <origin> (9 checks)
|
|
186
|
+
⊘ external skipped — pass --external (2 checks)
|
|
187
|
+
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Frontmatter
|
|
3
|
-
description: Every frontmatter field a page accepts — title, description, sidebar, SEO, search, and
|
|
3
|
+
description: Every frontmatter field a page accepts, all optional — title, description, sidebar, SEO, search, and the rest, with what each one controls.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Every page accepts the following frontmatter. All fields are optional.
|
|
@@ -81,4 +81,32 @@ changelog:
|
|
|
81
81
|
|
|
82
82
|
`date` may live here or at the top level — both feed the [changelog RSS feed](/docs/content#feeds). See [Changelog](/docs/advanced/changelog) for the generated timeline page and feed.
|
|
83
83
|
|
|
84
|
+
## Custom keys
|
|
85
|
+
|
|
86
|
+
Any key outside this reference fails the build, so typos are caught early. Projects that carry their own metadata can opt extra keys in via [`frontmatter.extend`](/docs/configuration#frontmatter) in `blume.config.ts`, each validated by a schema the project supplies:
|
|
87
|
+
|
|
88
|
+
```ts blume.config.ts lineNumbers
|
|
89
|
+
import { defineConfig } from "blume";
|
|
90
|
+
import { z } from "zod";
|
|
91
|
+
|
|
92
|
+
export default defineConfig({
|
|
93
|
+
frontmatter: {
|
|
94
|
+
extend: {
|
|
95
|
+
owner: z.string(),
|
|
96
|
+
reviewedAt: z.coerce.date().optional(),
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
```yaml page.mdx
|
|
103
|
+
---
|
|
104
|
+
title: Install
|
|
105
|
+
owner: "@sam"
|
|
106
|
+
reviewedAt: 2026-06-20
|
|
107
|
+
---
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Schemas are accepted through the [Standard Schema](https://standardschema.dev) interface, so Zod (whichever version your project installs), Valibot, and ArkType all work. Every declared key is validated on every page — absent ones included — so a required schema enforces the key site-wide; mark it `.optional()` to validate only where present. All other keys stay strictly validated, and built-in fields can't be redeclared.
|
|
111
|
+
|
|
84
112
|
Schemas are exported from `blume/schema` for editor and migration tooling.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blume",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Documentation that's fast, AI-ready, and zero-config.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"astro",
|
|
@@ -86,8 +86,6 @@
|
|
|
86
86
|
"@shikijs/twoslash": "^4.2.0",
|
|
87
87
|
"@tailwindcss/typography": "^0.5.20",
|
|
88
88
|
"@tailwindcss/vite": "^4",
|
|
89
|
-
"@takumi-rs/core": "^1.8.7",
|
|
90
|
-
"@takumi-rs/helpers": "^1.8.7",
|
|
91
89
|
"@vercel/analytics": "^2.0.1",
|
|
92
90
|
"ai": "^5.0.0",
|
|
93
91
|
"astro": "^7.0.2",
|
|
@@ -103,6 +101,7 @@
|
|
|
103
101
|
"katex": "^0.17.0",
|
|
104
102
|
"marked": "^18.0.5",
|
|
105
103
|
"mermaid": "^11.15.0",
|
|
104
|
+
"node-html-parser": "^9.0.0",
|
|
106
105
|
"pagefind": "^1.3.0",
|
|
107
106
|
"pathe": "^2.0.0",
|
|
108
107
|
"react": "^19.0.0",
|
|
@@ -111,6 +110,7 @@
|
|
|
111
110
|
"shiki": "^4.2.0",
|
|
112
111
|
"simple-icons": "^13.0.0",
|
|
113
112
|
"tailwindcss": "^4",
|
|
113
|
+
"takumi-js": "^2.2.1",
|
|
114
114
|
"tinyglobby": "^0.2.10",
|
|
115
115
|
"typescript": "^6.0.3",
|
|
116
116
|
"undici": "^8.6.0",
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: blume-migrate
|
|
3
|
+
description: Migrate an existing documentation site (Mintlify, Docusaurus, Fumadocs, Nextra, Starlight, or any docs framework) to Blume, the markdown-first docs framework on Astro. Translate the source config to blume.config.ts, restructure content into Blume's filesystem-derived navigation, rewrite JSX callouts to directives, convert icons to Lucide, and inline snippets. Use when the user asks to migrate/convert/port a docs repo to Blume, or when the repo has a docs.json/mint.json, docusaurus.config.*, meta.json with fumadocs, _meta.* with nextra, or an astro.config.* with starlight().
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Migrate to Blume
|
|
7
|
+
|
|
8
|
+
Blume is a **markdown-first** documentation framework on Astro/Vite. You drop Markdown/MDX into a folder and get navigation, search, theming, Open Graph images, and a component library with no app boilerplate — **the framework is the template**. There is no starter to clone; the only thing a project owns is its content and a `blume.config.ts`.
|
|
9
|
+
|
|
10
|
+
Your job is to convert a source docs repo into an **idiomatic** Blume project — not a 1:1 transliteration. Read this file, detect the source framework, open the matching `references/<framework>.md` for the exact mappings, and work the loop below. Report everything you drop or approximate.
|
|
11
|
+
|
|
12
|
+
## Migration philosophy
|
|
13
|
+
|
|
14
|
+
- **Target idiomatic Blume, not a mechanical port.** Prefer filesystem-derived navigation over an exhaustive explicit `navigation.sidebar`. Prefer `:::` directives over JSX callouts. Prefer Blume defaults over restating them in config.
|
|
15
|
+
- **Every field has a default; `{}` is a valid config.** Map only what the source _declares_. If the source uses a framework default, don't write it.
|
|
16
|
+
- **Drop chrome that has no Blume equivalent — and say so.** Navbar CTAs, footer columns, custom theming, dynamic redirects, and unmappable icons get reported to the user, not silently discarded or faked.
|
|
17
|
+
- **Convert, don't preserve.** Blume's page frontmatter schema is **strict** — unknown keys are build errors. A source-only frontmatter key must be mapped to a Blume key or removed (and reported), never left to "maybe validate."
|
|
18
|
+
|
|
19
|
+
## Migration workflow
|
|
20
|
+
|
|
21
|
+
1. **Detect the source framework** and read its reference file:
|
|
22
|
+
- `docs.json` / `mint.json` → **Mintlify** (`references/mintlify.md`) — the deepest, config-declared nav.
|
|
23
|
+
- `docusaurus.config.*` → **Docusaurus** (`references/docusaurus.md`).
|
|
24
|
+
- `meta.json` + `fumadocs-*` deps (content under `content/docs/`) → **Fumadocs** (`references/fumadocs.md`).
|
|
25
|
+
- `_meta.{js,ts,json}` + `nextra` deps → **Nextra** (`references/nextra.md`).
|
|
26
|
+
- `astro.config.*` calling `starlight({…})` → **Starlight** (`references/starlight.md`).
|
|
27
|
+
- Anything else → apply this file's mental model directly; there's no framework-specific reference, so inventory by hand.
|
|
28
|
+
- **Also note the host repo, independent of source framework:** a pnpm/Turbo workspace, a non-`docs/` content layout, or a Vercel deploy each need integration steps (`content.root` scoping, `minimumReleaseAge`, lockfile, `vercel.json`, an Astro/Vite patch) — all in `references/monorepo.md`. Read it whenever the target isn't a bare single-package docs folder.
|
|
29
|
+
2. **Inventory the repo** before changing anything: the config file(s), the content tree, the nav definition, snippets/partials/includes, static assets, OpenAPI/AsyncAPI specs, redirects, i18n locales, custom components, and icon usage. Note what's declared vs. defaulted.
|
|
30
|
+
3. **Write `blume.config.ts`** with `defineConfig` from `blume`. Map only declared fields (see the reference's mapping table); rely on defaults everywhere else. A minimal result is `defineConfig({ title: "…" })`.
|
|
31
|
+
4. **Restructure content.** Choose `content.root` (default `docs`) — **detect where `.md`/`.mdx` actually live, don't assume a `docs/` folder.** Many repos keep content directly under an app dir (`apps/docs/api/`, `.../getting-started/`) with no `docs/` subfolder; when so, set `content.root` to that dir and scope `content.include` to the real content folders rather than leaving a bare `content.root: "."` that scans everything (see `references/monorepo.md` §1). Order with numeric prefixes (`01-intro.mdx`), group without a URL segment via `(group)/` folders, and add a `meta.ts` (`defineMeta`) only where filesystem order isn't enough. **A source that already declares per-folder navigation in a sidecar file — Fumadocs `meta.json`, Nextra `_meta.*` — _is_ that case: convert each one to a `meta.ts`, carrying over its title/icon/order/collapse, rather than dropping it and hoping filenames reproduce the intent. Filesystem inference is the fallback for folders that declare no per-folder nav, never a reason to discard one that does.** Reach for an explicit `navigation.sidebar` only when the source nav genuinely can't be expressed by files. **Reshaping into folder-per-tab moves URLs** — track every old→new path as you go; you'll turn them into `redirects` in step 5.
|
|
32
|
+
5. **Rewrite pages.** Map frontmatter to Blume's strict schema; convert callout JSX to `:::` directives — **directives (and math/mermaid/package-install fences) are MDX-only, so rename any `.md` page that needs them to `.mdx`**; rename components; inline snippets/partials (Blume has no import-based includes); fix asset paths; **rewrite internal links** to their new routes (including OpenAPI operation links — see the OpenAPI section, their slugs differ from most sources); **add a `redirects` entry for every route you moved** in step 4; **convert every icon name to Lucide** (Blume is Lucide-only — no FontAwesome/Tabler). Remove any duplicated H1 in the body (`title` renders the H1; bodies start at `##`). **If the source has a hand-maintained changelog and the repo is open source on GitHub, offer to swap it for the `github-releases` source** (see "Changelogs" below) rather than porting the entries. For **Mintlify**, run the bundled codemod first — `node <skill>/scripts/mintlify-codemod.mjs --write <content-dir>` deterministically remaps icons and drops/renames unsupported frontmatter keys, and reports the rest (unknown icons, OpenAPI-stub flags) for you to finish by hand (see `references/mintlify.md`).
|
|
33
|
+
6. **Adopt `package.json`.** Repoint `dev`/`build`/`start` → `blume dev`/`blume build`/`blume preview`, remove the old framework's deps, add `blume`. A config-only source (e.g. a bare Mintlify `docs.json`) has no manifest — scaffold one. **In a pnpm workspace:** if `pnpm-workspace.yaml`/`.npmrc` sets `minimumReleaseAge`, add **only** `blume` to `minimumReleaseAgeExclude` (don't disable the guard) so the just-published version installs. **Always regenerate the lockfile in the same change:** after editing deps run a plain `pnpm install` (from the workspace root) and commit `pnpm-lock.yaml` alongside `package.json` — CI/Vercel use `--frozen-lockfile`, so a stale lockfile fails the build before it starts. **If the repo uses (or the user wants) [Ultracite](https://www.ultracite.ai) for formatting:** its oxfmt formatter mangles the `:::` directives you just wrote unless you ship the bundled `assets/oxfmt@0.55.0.patch` and register it under `patchedDependencies` — see `references/monorepo.md` §6. See `references/monorepo.md` §2–3.
|
|
34
|
+
7. **Wire up the host repo & deploy (non-trivial repos).** For a monorepo on Vercel, emit the root-aware install/build recipe and `apps/docs/vercel.json`, and tell the user the two settings you can't commit (Vercel Root Directory, Node 22). If the workspace pins Vite and `blume build` crashes inside Astro/Vite, apply the pnpm-patch workaround. All copy-pasteable in `references/monorepo.md` §4–5.
|
|
35
|
+
8. **Verify.** Run `blume build --strict` (frontmatter schema, duplicate routes, config — **without `--strict` a build exits 0 despite content errors**, silently dropping invalid pages) and `blume validate --strict` (internal links, heading anchors, assets — the link checker lives in `validate`, not `build`), fix diagnostics, then `blume dev` for a visual pass. End with a written summary of what was migrated, dropped, and approximated — **and every repo-specific edit you made** (pnpm-workspace, vercel.json, config globs) with the reason, plus any manual step left to the user (the Astro patch, Vercel dashboard settings).
|
|
36
|
+
|
|
37
|
+
## The Blume mental model
|
|
38
|
+
|
|
39
|
+
The single biggest shift for most sources — especially Mintlify — is that **navigation is derived from the filesystem**, not declared in config.
|
|
40
|
+
|
|
41
|
+
### Navigation is the file tree
|
|
42
|
+
|
|
43
|
+
- **Folders become groups, files become pages.** A page's sidebar label is its frontmatter `title`; a group's label is the humanized folder name.
|
|
44
|
+
- **Ordering resolves highest-priority-first:** an explicit `navigation.sidebar` (replaces the whole tree) → a folder's `meta.ts` `pages` array → a page's frontmatter `sidebar.order` → the filesystem (`index` first, then numeric filename prefix like `01-`, then alphabetical).
|
|
45
|
+
- **`meta.ts` refines one folder** (`defineMeta({ title, icon, order, collapsed, pages })`). The `pages` array lists children by slug (numeric prefix and parentheses stripped); children you omit fall back to their own `sidebar.order`, then filesystem order (`index` still sorts first) — so a partial `pages` list is safe, but list every child when the source declared a complete order.
|
|
46
|
+
- **Sidebar render mode is global, not per-folder.** `navigation.sidebar.display` in `blume.config.ts` is `"flat"` (default), `"group"` (collapsible), or `"page"` (drill-in sub-panel) and applies to **every** group at once. (It used to live on each folder's `meta.ts` as `display`; that field was **removed** — writing it in a `meta.ts` is now a build error. Set it once in config instead.) An explicit `navigation.sidebar` item may still override its own group's `display`.
|
|
47
|
+
- **An explicit `navigation.sidebar` replaces filesystem generation entirely.** Use it only for a nav shape files can't express. Its items are a page route string, a group (`{ label, items }`), or a link (`{ label, href }`).
|
|
48
|
+
- **Config-declared nesting has no on-disk counterpart — materialize it or it flattens silently.** When a source (Mintlify `groups`, Nextra `_meta`, a Docusaurus sidebar…) declares a nested group, its pages usually sit **flat in one folder** and the grouping lives only in config. Filesystem-derived nav sees the flat folder and drops the inner group. To keep the nesting you must **either** move those pages into a real subfolder (`meta.ts` for label/`collapsed`) — which changes their URLs, so add `redirects` — **or** declare the group in an explicit `navigation.sidebar`, which nests the existing routes without moving a file. Walk config `pages`/nav arrays **recursively** during inventory and record where config nesting depth exceeds on-disk depth; that gap is exactly what gets lost.
|
|
49
|
+
|
|
50
|
+
### Tabs and selectors
|
|
51
|
+
|
|
52
|
+
- **`navigation.tabs`** (`{ label, path, icon? }`) render top-of-header sections and **scope the sidebar by route** — the folder at a tab's `path` becomes the section, so this needs no config beyond the tabs themselves; structure content as **one folder per tab**. **A source's top-level tabs (Mintlify `navigation.tabs`, a top-level product/section switcher) map to these header tabs — keep them as tabs; don't flatten them into a single global `navigation.sidebar`.** Blume picks the active tab by **URL prefix** (longest tab `path` that prefixes the route), so every page in a tab must live under that tab's single `path`; a source tab that mixes arbitrary routes isn't portable as-is — either move its pages under one prefix (route change → add `redirects`) or accept the closest shape, and say which in the report (details in `references/mintlify.md`). The filtering runs both ways: on a route **under** a tab's `path`, the sidebar shows **only** that tab's folder (a tab also highlights when the current route is under it); on a **root or untabbed** route (or a tab whose `path` is `/`), the tab folders are **hidden** and the sidebar shows only the loose pages that belong to no tab (full tree as a fallback, so it's never blank). Consequence for migrations: once you add tabs, the landing sidebar automatically drops the sectioned content — that's intended, not lost pages; don't hand-build excludes for it.
|
|
53
|
+
- **`navigation.selectors`** (`{ kind, label, items: [{ label, path, icon?, description?, tag? }] }`, `kind` = `dropdown`/`product`/`version`/`language`) partition a whole site (products, versions) via a header dropdown keyed on the current route.
|
|
54
|
+
- **`navigation.featured`** (`{ label, href, icon? }`) pins links to the **top of the sidebar, above every section** — a blog, changelog, or support page that should always be one click away. These are the **exception to tab scoping**: unlike the generated tree, featured links show on **every** route and breakpoint. `href` points anywhere — an external URL opens in a new tab, an internal route (`/contact`) is validated against your pages at build time. `icon` is a Lucide name (or image path/URL/inline SVG), as everywhere else. This is the home for a source's always-visible header/utility links (Mintlify anchors, Blog/Contact links) — see `references/mintlify.md`.
|
|
55
|
+
|
|
56
|
+
### Routes and pathing
|
|
57
|
+
|
|
58
|
+
- A route is the content path relative to `content.root`, with **numeric prefixes stripped** (`01-intro.mdx` → `/intro`) and **`(group)/` folders adding no segment**. An `index` file maps to its folder's route. Frontmatter `slug` overrides the generated route.
|
|
59
|
+
|
|
60
|
+
### `blume.config.ts` shape
|
|
61
|
+
|
|
62
|
+
`defineConfig({...})` — every field optional, all with defaults:
|
|
63
|
+
|
|
64
|
+
- **Site:** `title`, `description`, `logo` (string SVG, or `{ image: string | { light, dark, alt }, text, href }`), `banner` (`{ content, link, dismissible, id }` — no color/type). A logo renders beside `title` in the header, so a **wordmark logo doubles the brand** ("Acme Acme") — set `text: ""` to render the mark alone. **Prefer the string form over `{ light, dark }`:** if you have the logo SVG locally and it's monochrome (solid black or white), rewrite its `fill`/`stroke` to `currentColor` and use `logo: "/logo.svg"` — it then inherits the theme's text color and adapts to light/dark automatically, so you don't need separate light/dark files.
|
|
65
|
+
- **`theme`:** `accent` (a color string for both modes, or `{ light, dark }` per mode), `action` (color), `mode` (`light`/`dark`/`system`), `radius`, `fonts` (`{ body, display, mono }` — curated Google-font slugs), `background` and `backgroundImage` (each a string, or `{ light, dark }` per mode). The old `accentDark`/`backgroundDark`/`backgroundImageDark` fields were **merged into these per-mode objects** — a bare string still applies to both modes, so only reach for `{ light, dark }` when the two modes differ. There is **no** `theme.strict` and **no** `theme.css` config field — custom CSS goes in a project-root **`theme.css` file** (auto-picked-up), and a source's "strict appearance" flags drop.
|
|
66
|
+
- **`content`:** `root` (default `"docs"`, relative to the project dir where `blume` runs), `include`/`exclude` (arrays of globs **relative to `content.root`**; defaults `["**/*.{md,mdx}"]` / `["**/_*", "**/.*"]`), `sources` (staged sources: `filesystem`, `github-releases`, `notion`, `sanity`, `mdx-remote`, `custom` — OpenAPI is **not** one of these; it's the top-level `openapi` field), `pages` (custom `.astro` dir), `defaultType`. When docs sit directly under the project dir (no `docs/` subfolder), set `root` there and **scope `include` to the real content folders** instead of scanning everything — `references/monorepo.md` §1.
|
|
67
|
+
- **`basePath`** (top-level): a site-wide mount point (e.g. `"/docs"`) prepended to **every** route while staying invisible to the sidebar (no wrapper group). This is the right target for a source that served all docs under a prefix (Docusaurus `routeBasePath`, a Fumadocs `baseUrl` of `/docs`) — distinct from a per-source `prefix` (which adds a nav group) and from `deployment.base` (host subdirectory).
|
|
68
|
+
- **`navigation`:** `tabs`, `selectors`, `featured` (links pinned above the sidebar on every route), `sidebar` (`{ display, items }` — `display` is the global render mode above; `items` is an explicit tree), `repo`. **Avoid an explicit `navigation.sidebar` unless you have to** — lean on the filesystem-derived sidebar. It only works when the file tree roughly matches the intended sidebar layout, so reshape folders to match first; reach for `sidebar.items` only for a shape files genuinely can't express (see "Config-declared nesting" above).
|
|
69
|
+
- **`search`** (Orama default, Pagefind opt-in), **`ai`** (llms.txt, Ask AI, the MCP server), **`openapi`**, **`redirects`**, **`seo`**, **`markdown`**, **`analytics`**, **`deployment`**, **`i18n`**, **`toc`**, **`lastModified`**, **`github`**.
|
|
70
|
+
- **Don't set `deployment.site`.** Blume auto-fills it: the dev server's `localhost` URL in dev, and the deployment URL (`VERCEL_PROJECT_PRODUCTION_URL`/`VERCEL_URL`) on Vercel. Hardcoding it in `blume.config.ts` overrides that auto-detection and pins the wrong absolute URL (canonical links, sitemap, OG, llms.txt) everywhere but the one host you typed — so leave it unset even when a source config had a `url`/`site` field. (Sitemap still generates in production because the deploy URL is present there.)
|
|
71
|
+
- **Favicon is a filename convention, not config.** Drop `icon`/`favicon.{svg,png,ico}` (and `apple-icon.png`) in the project root or `public/` — Blume auto-detects it. There is **no** `favicon` config field. A source favicon given as `{ light, dark }` **collapses to one** — pick a single file and report the loss.
|
|
72
|
+
|
|
73
|
+
The schema is exported from `blume/schema`; the full field reference is in `node_modules/blume/docs/configuration/`.
|
|
74
|
+
|
|
75
|
+
### Icons are Lucide, period
|
|
76
|
+
|
|
77
|
+
Blume resolves **bare kebab-case [Lucide](https://lucide.dev) names** everywhere an icon is accepted — frontmatter `icon`, `sidebar.icon`, `meta.ts` `icon`, `navigation.tabs`/`selectors` icons, and `Card`/`Step`/`Icon`/etc. props. There is **no** FontAwesome or Tabler support and **no** `iconType` prop. Names must be **kebab-case** (`book-open`, not `BookOpen`) — a PascalCase React-component name (common in Fumadocs/lucide-react sources) does not resolve and renders nothing. When migrating a source that uses another icon set (Mintlify defaults to FontAwesome), **map each name to its closest Lucide equivalent**; where none exists, drop the icon and report it. Verify a name exists at [lucide.dev/icons](https://lucide.dev/icons) before writing it.
|
|
78
|
+
|
|
79
|
+
### Page frontmatter (strict — unknown keys are build errors)
|
|
80
|
+
|
|
81
|
+
```yaml
|
|
82
|
+
---
|
|
83
|
+
title: Install # renders as the page H1 — remove any duplicate H1 in the body
|
|
84
|
+
description: Install Blume and scaffold your first project.
|
|
85
|
+
type: doc # doc (default) | blog | changelog | api
|
|
86
|
+
icon: download # a Lucide name
|
|
87
|
+
sidebar:
|
|
88
|
+
label: Install # overrides title in the sidebar
|
|
89
|
+
order: 2
|
|
90
|
+
icon: download
|
|
91
|
+
badge: New
|
|
92
|
+
hidden: false
|
|
93
|
+
seo:
|
|
94
|
+
title: …
|
|
95
|
+
description: …
|
|
96
|
+
image: /og/install.png
|
|
97
|
+
canonical: https://…
|
|
98
|
+
noindex: false
|
|
99
|
+
search:
|
|
100
|
+
exclude: false
|
|
101
|
+
tags: [api]
|
|
102
|
+
slug: install # override the generated route
|
|
103
|
+
draft: false
|
|
104
|
+
lastModified: 2026-06-20 # pin the "last updated" date
|
|
105
|
+
---
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Also valid: `date`/`authors` (blog/changelog feeds), `changelog` (changelog metadata), `deprecated`, `hidden`, `noindex`.
|
|
109
|
+
|
|
110
|
+
### Authoring features (no imports needed in `.mdx`)
|
|
111
|
+
|
|
112
|
+
- **The rich features are MDX-only.** Directives, `package-install`, mermaid, and math are wired into the MDX processor; in a plain `.md` file a `:::note` stays **literal text** — and the build stays green. **Rename any `.md` file that uses (or should use) these to `.mdx` during migration.** This bites hardest on Docusaurus/Starlight sources, whose `.md` content is full of `:::` admonitions. Plain Markdown (headings, tables, fenced code with titles/highlighting) is fine in `.md`.
|
|
113
|
+
- **Callouts as directives:** `:::note`, `:::tip`, `:::warning`, `:::danger`, `:::info`, `:::success`, with an optional title in brackets: `:::warning[Heads up]`. Aliases `caution`→warning, `error`→danger, `important`→note, `warn`→warning.
|
|
114
|
+
- **No-import MDX components:** `Callout`, `Card`/`CardGroup`, `Columns`/`Column`, `Steps`/`Step`, `Tabs`/`Tab`, `Accordion`/`AccordionItem`, `Expandable`, `FileTree`, `Tree`/`Tree.Folder`/`Tree.File`, `CodeGroup`, `Frame`, `Panel`, `Tooltip`, `Tile`, `Badge`, `Icon`, `TypeTable`/`AutoTypeTable`, `Color`, `YouTube`, `Visibility`, `GithubInfo`, `Component`, `CodeBlock`, `Diff`, `Prompt`, `Math`. (**Not** shipped — convert away: `<Warning>` → the `:::warning` directive, and the `ParamField`/`ResponseField`/`RequestField` field family → `TypeTable` rows or the OpenAPI reference. See the reference files for targets.)
|
|
115
|
+
- **Fenced-code superpowers:** ` ```package-install ` → package-manager tabs; ` ```mermaid ` → a rendered diagram; code-block titles (` ```ts server.ts `), line numbers (`lineNumbers`), and highlighting (`{1,4-5}`, `// [!code ++]`).
|
|
116
|
+
- **Math:** block math `$$…$$` renders in `.mdx` with **no config** (there is no `markdown.math` field). Inline `$…$` is **not** supported — a bare `$` stays literal text; convert inline math to display math or drop it (report).
|
|
117
|
+
|
|
118
|
+
### OpenAPI
|
|
119
|
+
|
|
120
|
+
`openapi: { enabled: true, sources: [{ spec, label?, route? }] }` generates **one real page per operation** — with routing, sidebar, search, and OG images for free. **The reference does not get a header tab automatically** — add a `navigation.tabs` entry pointing at the reference's `route` (reference routes are valid tab targets) or the API reference is unreachable from the header. **Never hand-migrate generated API-reference pages** (per-endpoint stub pages in the source): delete them and point `openapi.sources` at the spec. (`renderer: "scalar"` keeps the Scalar embed instead; AsyncAPI uses the same embed.)
|
|
121
|
+
|
|
122
|
+
- **Vendor the spec by default.** A remote `spec:` URL makes every build depend on fetching it at build time — a single point of failure in CI, offline, or behind a proxy, and a failed fetch skips the whole reference. Prefer committing the spec into the repo (`openapi/<name>.json`) and pointing `spec` at the local path; if you keep the URL, say so and consider a `prebuild` step that refreshes the local copy with a fallback.
|
|
123
|
+
- **Operation routes have their own slug scheme** — `<route>/<slugified-tag>/<slugified-operationId>` (e.g. tag `Models`, id `listModels` → `/api-reference/models/listmodels`). This rarely matches the source's endpoint links (Mintlify/others kebab-case differently), so **rewrite every inbound link to an operation**. `blume validate` resolves operation pages like any other route, so it catches the ones you miss.
|
|
124
|
+
- **Keep hand-written conceptual pages.** Sources often pair a written "Introduction/Authentication" page with the endpoint group in the same tab. A normal content page placed under the openapi `route` merges into the reference tab's sidebar — so keep those (auth, errors, rate limits) and delete only the per-endpoint stubs.
|
|
125
|
+
|
|
126
|
+
### Changelogs
|
|
127
|
+
|
|
128
|
+
If the source ships a **hand-maintained changelog** (a `changelog.mdx`, a folder of dated entries, Mintlify `<Update>` blocks) **and the project is open source on GitHub**, offer to replace it with the **`github-releases`** content source — release notes become the changelog automatically, with no files to maintain. It's an offer, not an automatic rewrite: some teams keep a curated changelog that doesn't map 1:1 to GitHub releases, so confirm the release notes are the source of truth before deleting their pages.
|
|
129
|
+
|
|
130
|
+
Add it under `content.sources` alongside the filesystem source:
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
content: {
|
|
134
|
+
sources: [
|
|
135
|
+
{ include: ["docs/**/*.mdx"], root: ".", type: "filesystem" },
|
|
136
|
+
{
|
|
137
|
+
owner: "haydenbleasel",
|
|
138
|
+
repo: "ultracite",
|
|
139
|
+
prefix: "changelog",
|
|
140
|
+
type: "github-releases",
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
},
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
- Each release materializes as a `type: changelog` page under `/<prefix>/` (`prefix: "changelog"` → `/changelog/…`); omit `prefix` to mount at the root.
|
|
147
|
+
- Optional fields: `limit` (cap materialized releases, newest-first, default 100), `prereleases` (include prereleases), `drafts` (include drafts — needs a token with repo write access), `pollInterval` (dev polling seconds; omit to freeze for the session).
|
|
148
|
+
- A **private** repo reads a token from `GITHUB_TOKEN`; it is never inlined in config. A public repo needs no token.
|
|
149
|
+
- **Delete the old changelog pages** once the source is wired (and add `redirects` from their old routes to the new `/<prefix>/…` slugs). Pin a header/sidebar link with `navigation.featured` if the source had one.
|
|
150
|
+
|
|
151
|
+
### Redirects are static
|
|
152
|
+
|
|
153
|
+
A `redirects: [{ from, to, status? }]` array **in `blume.config.ts`** maps old URLs when you restructure routes — Blume serves these itself, so any reorganization that moves a page (folder-per-tab, materialized nested groups, renamed slugs, index promotion) is fixed by adding an entry there; no host config needed. **Restructuring is the main source of these:** every page you moved in step 4 (folder-per-tab, renamed slugs, index promotion) needs an entry, or old URLs 404. `status` defaults to **301 (permanent — browsers cache it indefinitely)**; that's correct for genuine moves, but never use 301/308 for redirects you might reverse. Dynamic/wildcard patterns (`:slug*`) can't be modeled as static path-to-path; move those to host-level config (`_redirects`, `vercel.json`) and report them.
|
|
154
|
+
|
|
155
|
+
## Verification & reporting
|
|
156
|
+
|
|
157
|
+
1. Run **`blume build --strict`** — it validates the frontmatter schema, duplicate routes, and config, and `--strict` makes diagnostics fail the build (without it, `blume build` **exits 0 despite content errors** and silently drops invalid pages). Then run **`blume validate --strict`** — links, heading anchors, and assets live here, not in `build` (add `--external` to also check outbound HTTP links). OpenAPI operation pages are real routes to `validate`, so dead links to them are caught too. Iterate until both are clean.
|
|
158
|
+
2. Run `blume dev` and review the site visually — nav structure, tabs, theme, rendered components.
|
|
159
|
+
3. **Write a migration summary** covering: what was migrated (config, N pages, nav, OpenAPI), what was **dropped** (navbar CTAs, footers, custom theming, dynamic redirects, unmappable icons, unsupported components), and suggested follow-ups (`blume eject` for full control, `blume add` to vendor a component for customization).
|
|
160
|
+
|
|
161
|
+
## Full documentation
|
|
162
|
+
|
|
163
|
+
The mapping details live in `references/`: one file per source framework (`mintlify.md`, `docusaurus.md`, `fumadocs.md`, `nextra.md`, `starlight.md`), plus **`monorepo.md`** for host-repo integration (content-layout detection, pnpm `minimumReleaseAge`, frozen-lockfile regeneration, the Vercel monorepo recipe, and the Astro/Vite patch). The Mintlify icon + frontmatter pass is automated by **`scripts/mintlify-codemod.mjs`** (zero-dependency, deterministic, idempotent; `--write` to apply). The authoritative Blume docs are bundled in the installed package at **`node_modules/blume/docs`** (or `apps/docs/content/docs` in a repo checkout). The most relevant pages:
|
|
164
|
+
|
|
165
|
+
- `configuration/index.mdx` — every `blume.config.ts` field.
|
|
166
|
+
- `content/navigation.mdx` — the sidebar/tabs/selectors model.
|
|
167
|
+
- `content/meta.mdx` — `meta.ts` and display modes.
|
|
168
|
+
- `content/syntax.mdx` — directives, code features, math.
|
|
169
|
+
- `content/components.mdx` — the component library and APIs.
|
|
170
|
+
- `reference/frontmatter.mdx` — the strict page schema.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
diff --git a/dist/markdown-Bowe09rB.js b/dist/markdown-Bowe09rB.js
|
|
2
|
+
index 0fe63ff712e07c9c2188711fa0940f7a7d8f7d74..44435d91a2f5a6711512981c9bcaddedc1760be4 100644
|
|
3
|
+
--- a/dist/markdown-Bowe09rB.js
|
|
4
|
+
+++ b/dist/markdown-Bowe09rB.js
|
|
5
|
+
@@ -1141,7 +1141,14 @@ function Yf(e, r, t) {
|
|
6
|
+
case "sentence": return Yi(e, t);
|
|
7
|
+
case "word": return Li(e);
|
|
8
|
+
case "whitespace": {
|
|
9
|
+
- let { next: i } = e, u = i && /^>|^(?:[*+-]|#{1,6}|\d+[).])$/u.test(i.value) ? "never" : r.proseWrap;
|
|
10
|
+
+ let { next: i, previous: oxfmtFencePrev } = e;
|
|
11
|
+
+ // Preserve line breaks that sit directly against a `:::` container
|
|
12
|
+
+ // directive fence, so `proseWrap: "never"` keeps the opening/closing
|
|
13
|
+
+ // fence on their own lines instead of joining them into the prose (which
|
|
14
|
+
+ // breaks the directive). Ordinary prose still wraps per proseWrap.
|
|
15
|
+
+ // See prettier/prettier#19040.
|
|
16
|
+
+ let oxfmtIsFence = (w) => w != null && typeof w.value === "string" && w.value.startsWith(":::");
|
|
17
|
+
+ let u = oxfmtIsFence(oxfmtFencePrev) || oxfmtIsFence(i) ? "preserve" : i && /^>|^(?:[*+-]|#{1,6}|\d+[).])$/u.test(i.value) ? "never" : r.proseWrap;
|
|
18
|
+
return qt(e, n.value, u);
|
|
19
|
+
}
|
|
20
|
+
case "emphasis": {
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Docusaurus → Blume
|
|
2
|
+
|
|
3
|
+
Docusaurus is a React site; navigation comes from `sidebars.js` (explicit or autogenerated) plus per-folder `_category_.json`. Admonitions are already directive syntax, which eases the content transform — **but note the source major version**: v2 (MDX v1) and v3 (MDX v3) have different content hazards, called out below.
|
|
4
|
+
|
|
5
|
+
## Detect
|
|
6
|
+
|
|
7
|
+
- **`docusaurus.config.{js,ts}`** at the root; a **`sidebars.{js,ts}`** file and per-folder **`_category_.json`** files.
|
|
8
|
+
- Content under **`docs/`** (often plus `blog/`, `src/pages/`, `static/`, `versioned_docs/`); `@docusaurus/*` deps.
|
|
9
|
+
- v2 vs v3: check the `@docusaurus/core` major in `package.json`.
|
|
10
|
+
|
|
11
|
+
## Config: `docusaurus.config` → `blume.config.ts`
|
|
12
|
+
|
|
13
|
+
Read `themeConfig`, `presets`, and `plugins`:
|
|
14
|
+
|
|
15
|
+
| Docusaurus | Blume |
|
|
16
|
+
| --- | --- |
|
|
17
|
+
| `title` | `title` |
|
|
18
|
+
| `tagline` | `description` |
|
|
19
|
+
| `themeConfig.navbar.title` / `.logo` | `title` / `logo` (move the image into `public/`) |
|
|
20
|
+
| `themeConfig.navbar.items` (doc items) | `navigation.tabs` (for section links) |
|
|
21
|
+
| `themeConfig.navbar.items` (external/utility links — Blog, GitHub, Discord…) | **`navigation.featured`** (`{ label, href, icon? }`, pinned above the sidebar on every route); the GitHub link → the `github` config instead |
|
|
22
|
+
| `themeConfig.colorMode.defaultMode` | `theme.mode` (`respectPrefersColorScheme: true` → `"system"`) |
|
|
23
|
+
| `themeConfig.prism.theme` / `.darkTheme` | `markdown.codeBlocks.theme: { light, dark }` (map Prism theme names to Shiki themes, e.g. `github`/`github-dark`) |
|
|
24
|
+
| `themeConfig.metadata` / `themeConfig.image` | per-page `seo` frontmatter / `seo.og`; report what doesn't fit |
|
|
25
|
+
| `url` + `baseUrl` | **`url` → drop** (`deployment.site` auto-detects); `baseUrl` (when not `/`) → `deployment.base` |
|
|
26
|
+
| preset `docs.routeBasePath` — **including the default!** | Docusaurus serves docs at **`/docs/…` by default**; the "map only declared fields" rule does **not** apply here because the _URLs_ are load-bearing. Either keep them with top-level **`basePath: "/docs"`** (invisible to the sidebar), or intentionally move to root and emit a `redirects` entry per page. Decide explicitly and say which. (`routeBasePath: '/'` = docs-only mode — nothing to do.) |
|
|
27
|
+
| preset `docs.editUrl` | `github` (owner/repo/branch; a path after the branch → `github.dir`) |
|
|
28
|
+
| `themeConfig.footer` | drop → Footer override (`defineComponents` layout slot) |
|
|
29
|
+
| `themeConfig.announcementBar` | `banner` (`{ content, dismissible, id }` — `isCloseable` → `dismissible`; colors drop) |
|
|
30
|
+
| `i18n.locales` / `defaultLocale` | `i18n` — translated files live at `i18n/<locale>/docusaurus-plugin-content-docs/current/…`; move them to `<locale>/…` under `content.root` |
|
|
31
|
+
| `themeConfig.algolia` | drop — Blume ships built-in search (Orama); remove the Algolia dep |
|
|
32
|
+
| `@docusaurus/plugin-client-redirects` | **static `redirects: [{from, to}]` arrays convert 1:1** to Blume `redirects` (a `from` array = one entry per item); only `createRedirects` functions are truly dynamic → host rules |
|
|
33
|
+
| `@docusaurus/theme-mermaid` | delete the dep — ` ```mermaid ` renders natively (in `.mdx`) |
|
|
34
|
+
| `remark-math` + `rehype-katex` | delete — block `$$…$$` renders in `.mdx` with no config (no `markdown.math` field exists); **inline `$…$` is not supported** — convert or drop (report) |
|
|
35
|
+
| Multi-instance docs plugins (`plugin-content-docs` with `id`) | one folder (and usually one `navigation.tabs` entry) per instance |
|
|
36
|
+
|
|
37
|
+
## Static assets — required move
|
|
38
|
+
|
|
39
|
+
Every Docusaurus repo serves **`static/`** at the site root (`static/img/foo.png` → `/img/foo.png`). Blume serves **`public/`**. **Move `static/*` (and any extra `staticDirectories`) into `public/`** — all root-relative references then keep resolving unchanged. Without this, every image and download 404s.
|
|
40
|
+
|
|
41
|
+
## Navigation: `sidebars.js` + `_category_.json`
|
|
42
|
+
|
|
43
|
+
- **Autogenerated sidebar** (`{ type: 'autogenerated', dirName: '...' }`) → Blume's default filesystem navigation. Docusaurus strips numeric prefixes (`01-`) exactly like Blume, so the convention round-trips; no config needed.
|
|
44
|
+
- **Explicit sidebar** (arrays of doc IDs, categories, links) → restructure into folders where possible; use `navigation.sidebar` only for shapes files can't express. A category `{ type: 'category', label, items }` → a folder (label → `meta.ts` `title`); `collapsed` → `meta.ts` `collapsed` (**no `display` field in `meta.ts`** — if collapse matters, set the global `navigation.sidebar.display: "group"` once in config).
|
|
45
|
+
- **`_category_.json`** (also `.yml`) (`{ label, position, collapsed, collapsible, link, className, customProps }`) → a folder `meta.ts`: `label`→`title`, `position`→`order`, `collapsed`→`collapsed`. `link.type: 'generated-index'` → an `index` page in the folder — **and the old URL was `/docs/category/<slug>`**, so add a redirect and rewrite inbound links. `link.type: 'doc'` → make that doc the folder's `index`. `collapsible`/`className`/`customProps` → drop (report).
|
|
46
|
+
- **`src/pages/` — inventory, don't nuke.** Nearly every repo has a React landing page (`src/pages/index.tsx`) and often extra Markdown pages. Markdown pages → content pages; the React home page → rebuild as a docs index or a custom `.astro` page under `content.pages` — report either way.
|
|
47
|
+
|
|
48
|
+
## Versioned docs
|
|
49
|
+
|
|
50
|
+
Docusaurus `versioned_docs/version-X/` + `versions.json` (+ `versioned_sidebars/`) → **recommend migrating the latest released version only**. Mind the URL scheme: by default the **latest release** serves at `/docs/` and the work-in-progress `docs/` folder serves at `/docs/next` (`lastVersion: 'current'` flips this) — pick the folder that matches what users see at `/docs/`. If older versions must stay, put each under its own folder and wire a `navigation.selectors` entry of `kind: "version"`.
|
|
51
|
+
|
|
52
|
+
## Blog
|
|
53
|
+
|
|
54
|
+
A Docusaurus `blog/` → Blume `type: blog` pages. **Dates come from filenames/folders** (`2024-01-31-foo.md`) — extract each into `date` frontmatter and strip the date from the filename (the old dated URLs `/blog/2024/01/31/foo` need `redirects`). Strip `<!-- truncate -->` / `{/* truncate */}` markers. `authors.yml` refs → inline author objects in each post's `authors` frontmatter. RSS stays at `/blog/rss.xml` on both sides.
|
|
55
|
+
|
|
56
|
+
## Content & components
|
|
57
|
+
|
|
58
|
+
- **`.md` vs `.mdx` — both majors need renames, for opposite reasons.** Blume parses `.md` as plain Markdown: no directives, no JSX, no `$$` math, no mermaid/package-install fences. **v3** treats `.md` as MDX (so a `.md` with imports/JSX/`{}` renders them as literal text in Blume); **v2** content is looser MDX v1. Rule: **rename any `.md` that contains admonitions, JSX, imports, or math to `.mdx`** — for typical Docusaurus repos that is most files.
|
|
59
|
+
- **Admonitions are directives — but check the version.** v3: `:::note`, `:::tip`, `:::info`, `:::warning`, `:::danger` pass through; `:::caution` → `:::warning` (or rely on Blume's alias); titles `:::note[Title]` work. **v2:** titles are space-separated (`:::note Your Title`) — rewrite to brackets or the title is silently lost; and v2's `:::warning` rendered **red/danger** — audit whether it should become `:::danger`.
|
|
60
|
+
- **Tabs:** `<Tabs>`/`<TabItem label="…" value="…">` → `<Tabs>`/`<Tab title="…">`. Drop `groupId`/`queryString`/`value`; strip the `@theme/Tabs` imports.
|
|
61
|
+
- **Theme JSX in content:** `<DocCardList/>` (standard on category index pages) → hand-write `Card`/`CardGroup` links or delete (a Blume group page lists its children); `<TOCInline/>` → drop (report); `<CodeBlock>` JSX → a fenced code block; `<Admonition>` → the matching directive; `<details>`/`<summary>` → `<Accordion>`/`<AccordionItem>` or leave as raw HTML.
|
|
62
|
+
- **`@theme/*` / `@site/*` imports** — strip `@theme/*` (Blume injects components globally); rewrite `@site/` asset/module paths to `/public` URLs or inline. **MDX partials** (`_partial.mdx` imports) → inline the partial's body (Blume's default `**/_*` exclude already hides the partial files themselves).
|
|
63
|
+
- **Code blocks:** `title="file.js"` → works as-is; `showLineNumbers` → `lineNumbers`; **magic comments** (`// highlight-next-line`, `highlight-start`/`end`) → `{ranges}` or `// [!code highlight]` — unconverted they ship as literal comments in every sample; ` ```bash npm2yarn ` → ` ```package-install `.
|
|
64
|
+
- **MDX v1 (v2 sources) pitfalls:** unescaped `<`/`{` in prose, HTML comments `<!-- -->` (→ `{/* */}`), string `style="…"` attributes (→ objects). Fix as build errors surface.
|
|
65
|
+
|
|
66
|
+
## Frontmatter
|
|
67
|
+
|
|
68
|
+
| Docusaurus | Blume |
|
|
69
|
+
| --- | --- |
|
|
70
|
+
| `title` / `description` | pass through |
|
|
71
|
+
| `id` | usually drop (routing is filesystem-based); use `slug` to pin a route |
|
|
72
|
+
| `slug` | `slug` |
|
|
73
|
+
| `sidebar_label` | `sidebar.label` |
|
|
74
|
+
| `sidebar_position` | `sidebar.order` |
|
|
75
|
+
| `unlisted` | `hidden: true` + `noindex: true` |
|
|
76
|
+
| `last_update.date` | `lastModified` |
|
|
77
|
+
| `draft` | `draft` |
|
|
78
|
+
| `tags` | `search.tags` (or drop if only used for the blog tag index) |
|
|
79
|
+
| `image` | `seo.image` |
|
|
80
|
+
| `keywords` | drop → use `seo` |
|
|
81
|
+
| `sidebar_class_name` / `sidebar_custom_props` / `pagination_next` / `pagination_prev` / `pagination_label` / `hide_title` / `hide_table_of_contents` / `toc_min_heading_level` / `toc_max_heading_level` / `displayed_sidebar` / `custom_edit_url` | drop (report) |
|
|
82
|
+
|
|
83
|
+
Remove any duplicated H1 in the body — `title` renders the H1.
|
|
84
|
+
|
|
85
|
+
## Icons
|
|
86
|
+
|
|
87
|
+
Docusaurus has no built-in icon-name system (icons are usually inline SVG/emoji/CSS). Where you add sidebar/tab icons, use Lucide names. Convert any FontAwesome/react-icons usage in content to Lucide `<Icon>` or drop it.
|
|
88
|
+
|
|
89
|
+
## Teardown
|
|
90
|
+
|
|
91
|
+
Remove `@docusaurus/*` and Algolia deps; delete `docusaurus.config.*`, `sidebars.*`, `babel.config.js`, and `src/` theme customizations (after the `src/pages/` inventory above; re-implement what matters via `defineComponents` layout slots or `blume eject`). Repoint `start`/`build`/`serve` scripts → `blume dev`/`blume build`/`blume preview`; add `blume`.
|
|
92
|
+
|
|
93
|
+
## Dropped — report these
|
|
94
|
+
|
|
95
|
+
Custom/swizzled theme components (layout slots or `blume eject`), footer columns, Algolia config, `sidebar_custom_props` and the other dropped frontmatter keys, `createRedirects` functions (→ host rules), React pages under `src/pages/`, `<TOCInline>`, per-category `className`/`customProps`, and any `@theme/*` component with no Blume equivalent.
|