blume 0.4.0 → 0.5.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/dist/cli/index.js +1137 -722
- package/dist/cli/index.js.map +28 -23
- package/dist/types/core/data.d.ts +2 -0
- package/dist/types/core/project.d.ts +12 -2
- package/dist/types/core/schema.d.ts +154 -15
- package/dist/types/core/types.d.ts +7 -0
- package/docs/advanced/api-reference.mdx +33 -23
- package/docs/advanced/bridge.mdx +74 -0
- package/docs/advanced/meta.ts +8 -1
- package/docs/advanced/migrate.mdx +119 -0
- package/docs/configuration/index.mdx +1 -1
- package/docs/content/components.mdx +55 -2
- package/docs/content/i18n.mdx +1 -1
- package/docs/content/syntax.mdx +2 -2
- package/docs/index.mdx +2 -2
- package/docs/reference/cli.mdx +29 -1
- package/docs/reference/frontmatter.mdx +5 -0
- package/package.json +11 -1
- package/src/astro/generate.ts +18 -8
- package/src/astro/templates.ts +28 -4
- package/src/cli/commands/build.ts +107 -63
- package/src/cli/commands/check.ts +20 -0
- package/src/cli/dev-lock.ts +13 -5
- package/src/cli/prepare.ts +3 -0
- package/src/components/BlumePage.astro +6 -0
- package/src/components/Icon.astro +13 -10
- package/src/components/content/ApiField.astro +75 -0
- package/src/components/content/ParamField.astro +39 -0
- package/src/components/content/RequestField.astro +23 -0
- package/src/components/content/ResponseField.astro +23 -0
- package/src/components/content/Step.astro +1 -1
- package/src/components/layout/Breadcrumbs.astro +7 -2
- package/src/components/layout/NavTree.astro +24 -8
- package/src/components/layout/RootLayout.astro +56 -34
- package/src/components/layout/Search.astro +1 -1
- package/src/components/openapi/ApiOverview.astro +84 -0
- package/src/components/openapi/MethodBadge.astro +28 -0
- package/src/components/openapi/Operation.astro +140 -0
- package/src/components/openapi/ParametersTable.astro +97 -0
- package/src/components/openapi/RequestBody.astro +58 -0
- package/src/components/openapi/RequestPanel.astro +169 -0
- package/src/components/openapi/Responses.astro +91 -0
- package/src/components/openapi/SchemaProperty.astro +118 -0
- package/src/components/openapi/SchemaTable.astro +86 -0
- package/src/components/openapi/helpers.ts +238 -0
- package/src/components/openapi/panel.ts +59 -0
- package/src/components/openapi/snippets.ts +201 -0
- package/src/core/builtin-tags.ts +5 -0
- package/src/core/data.ts +2 -0
- package/src/core/project-graph.ts +5 -1
- package/src/core/project.ts +25 -3
- package/src/core/schema.ts +47 -6
- package/src/core/sources/mintlify.ts +1 -1
- package/src/core/sources/resolve.ts +28 -6
- package/src/core/types.ts +7 -0
- package/src/migrate/mintlify/config.ts +153 -1
- package/src/migrate/mintlify/content.ts +8 -2
- package/src/migrate/mintlify/index.ts +58 -1
- package/src/openapi/model.ts +174 -0
- package/src/openapi/parse.ts +48 -0
- package/src/openapi/references.ts +164 -0
- package/src/openapi/render-mdx.ts +76 -0
- package/src/openapi/scalar.ts +15 -103
- package/src/openapi/source.ts +140 -0
- package/src/registry/eject.ts +15 -2
- package/src/theme/chrome-icons.ts +22 -0
- package/src/theme/icons.ts +151 -161
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Migrate
|
|
3
|
+
description: Convert a Mintlify, Fumadocs, Nextra, or Starlight project to Blume in one command — config, content, navigation, and assets translated to idiomatic Blume.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Already have a docs site somewhere else? `blume migrate` is a one-shot codemod that converts a project from another docs framework into Blume — translating the config, rewriting each page to idiomatic Blume MDX, converting navigation files, and relocating assets, all in place. When it finishes you have a `blume.config.ts` and a tree that `blume dev` can serve.
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npx blume migrate <tool>
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
The `<tool>` is the framework you're coming from:
|
|
13
|
+
|
|
14
|
+
| Tool | Detects | Command |
|
|
15
|
+
| --- | --- | --- |
|
|
16
|
+
| [Mintlify](#mintlify) | `docs.json` / `mint.json` | `blume migrate mintlify` |
|
|
17
|
+
| [Fumadocs](#fumadocs) | `content/docs` + `meta.json` | `blume migrate fumadocs` |
|
|
18
|
+
| [Nextra](#nextra) | `content/` or `pages/` + `_meta` | `blume migrate nextra` |
|
|
19
|
+
| [Starlight](#starlight) | `src/content/docs` + `astro.config.*` | `blume migrate starlight` |
|
|
20
|
+
|
|
21
|
+
:::warning A migration **rewrites files in place** — pages, config, navigation, and assets. Commit (or stash) your work first so you can review the diff and roll back cleanly. :::
|
|
22
|
+
|
|
23
|
+
## What a migration does
|
|
24
|
+
|
|
25
|
+
Every migrator runs the same shape of work, tuned to the source framework:
|
|
26
|
+
|
|
27
|
+
<Steps>
|
|
28
|
+
<Step title="Translate the config">
|
|
29
|
+
The framework's config — `docs.json`, a `starlight({...})` block, and so on — becomes a `blume.config.ts` at your project root, with navigation, theme, and site chrome mapped across.
|
|
30
|
+
</Step>
|
|
31
|
+
<Step title="Rewrite every page">
|
|
32
|
+
Each `.md`/`.mdx` file is rewritten to idiomatic Blume markup: framework callouts become [`:::` directives](/docs/content/syntax), components are renamed or converted, and frontmatter is mapped to Blume's [page schema](/docs/reference/frontmatter). Keys Blume doesn't recognize are dropped and reported.
|
|
33
|
+
</Step>
|
|
34
|
+
<Step title="Convert navigation">
|
|
35
|
+
Navigation files (`_meta.{js,ts,json}`, `meta.json`) become typed [`meta.ts`](/docs/content/meta) files, preserving order and titles.
|
|
36
|
+
</Step>
|
|
37
|
+
<Step title="Relocate assets and scripts">
|
|
38
|
+
Referenced asset folders are kept in place and served via [`content.assets`](/docs/content/sources); loose top-level files move under `public/`. Your `dev`/`build`/`start` npm scripts are repointed at the matching `blume` commands.
|
|
39
|
+
</Step>
|
|
40
|
+
</Steps>
|
|
41
|
+
|
|
42
|
+
When it's done the CLI prints how many files it touched, a list of **warnings** — anything that needs a human eye, like a component with no Blume equivalent or a dropped frontmatter key — and a reminder to review the generated config:
|
|
43
|
+
|
|
44
|
+
```txt
|
|
45
|
+
✔ Migrated 42 content file(s).
|
|
46
|
+
⚠ Components without a Blume equivalent need manual review: <Frame>.
|
|
47
|
+
⚠ Dropped unsupported page frontmatter keys: mode, "og:image".
|
|
48
|
+
▶ Review blume.config.ts and run `blume dev`.
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Tools
|
|
52
|
+
|
|
53
|
+
### Mintlify
|
|
54
|
+
|
|
55
|
+
Reads `docs.json` (or legacy `mint.json`) and rewrites every page **in place** — content stays at the project root (`content.root` is `"."`).
|
|
56
|
+
|
|
57
|
+
- Snippets under `/snippets` are inlined as includes; component snippets (e.g. `.jsx`) are kept and their imports rewritten to relative paths.
|
|
58
|
+
- A Mintlify `openapi` spec — declared top-level or on a nav group (a path, URL, or `{ source, directory }`) — maps to Blume's [native OpenAPI reference](/docs/advanced/api-reference) (`openapi.sources`), which renders one real page per operation. A group's `directory` becomes the reference's route; endpoint refs like `GET /users` are dropped since Blume generates them from the spec.
|
|
59
|
+
- `docs.json` `variables` are inlined into content — Blume has no runtime `{{variable}}` substitution.
|
|
60
|
+
- Multi-language projects map to [`i18n.locales`](/docs/content/i18n); the language nav selector is dropped in favor of Blume's locale switching.
|
|
61
|
+
- Icons resolve against the real bundled libraries — Font Awesome (free), Lucide, and Tabler. The migrator sets [`icons.library: fontawesome`](/docs/content/components#default-library) (Mintlify's default), so Font Awesome names (`shield-halved`, `gauge-high`, `layer-group`, …) and `iconType` styles render unchanged. Pro-only FA styles (`light`/`thin`/`duotone`/`sharp-solid`) fall back to solid.
|
|
62
|
+
- Fonts map to [`theme.fonts`](/docs/configuration/theming) when the family is one of Blume's curated Google Fonts (`fonts.family`, or a `heading`/`body` split); a family outside that set is warned about, not guessed. Header links (`navbar.links`/`navbar.primary`) and footer socials (`footer.socials`) have no `blume.config` equivalent yet, so they're reported as warnings rather than dropped silently — re-add them with [`navigation.tabs`](/docs/content/navigation) or a Header/Footer [layout override](/docs/advanced/custom-pages). The contextual page menu and last-updated timestamp are already Blume defaults.
|
|
63
|
+
- Field components — [`<ParamField>`, `<ResponseField>`, `<RequestField>`](/docs/content/components#api-fields) — render natively via Blume's compat components, so hand-written CLI/SDK/endpoint field docs carry over unchanged. Any remaining component with no Blume equivalent (e.g. `<Update>`) is flagged for manual review.
|
|
64
|
+
|
|
65
|
+
:::tip Want to preview Blume against a Mintlify codebase **without** rewriting anything? Run `blume dev` with no config and Blume serves `docs.json` as-is in [Bridge mode](/docs/advanced/bridge). Migrate when you're ready to commit. :::
|
|
66
|
+
|
|
67
|
+
### Fumadocs
|
|
68
|
+
|
|
69
|
+
Reads `content/docs` + `meta.json`. Moves pages into `docs/`, preserving the `/docs` route prefix, and rewrites Fumadocs MDX to Blume markup: callouts, `<Cards>`/`<Accordions>`/`<Files>`, `<Tabs items>`, and `<include>` directives are all converted. Each `meta.json` becomes a typed `meta.ts`.
|
|
70
|
+
|
|
71
|
+
### Nextra
|
|
72
|
+
|
|
73
|
+
Reads `content/` or `pages/` plus `_meta` files. Moves pages into `docs/`, rewrites `<Callout>`s to [directives](/docs/content/syntax), and converts every `_meta.{js,ts,json}` into a typed `meta.ts` — navigation order and titles preserved.
|
|
74
|
+
|
|
75
|
+
### Starlight
|
|
76
|
+
|
|
77
|
+
Reads `src/content/docs` + your `astro.config.*`. Translates the `starlight({...})` options into `blume.config.ts` and rewrites each page **in place** (content stays under `src/content/docs`): asides become directives, components are renamed, and frontmatter is mapped across.
|
|
78
|
+
|
|
79
|
+
## After migrating
|
|
80
|
+
|
|
81
|
+
<Steps>
|
|
82
|
+
<Step title="Review the config">
|
|
83
|
+
Open `blume.config.ts` and check the mapped navigation, theme, and site
|
|
84
|
+
settings. The migrator is thorough but conservative — some source options
|
|
85
|
+
have no Blume equivalent and are left out.
|
|
86
|
+
</Step>
|
|
87
|
+
<Step title="Read the warnings">
|
|
88
|
+
Each warning points at something the codemod couldn't fully translate. Work
|
|
89
|
+
through them before shipping.
|
|
90
|
+
</Step>
|
|
91
|
+
<Step title="Run the dev server">
|
|
92
|
+
Serve the migrated site. Blume [validates your content](/docs/reference/cli#validating-links) as it serves it, so broken links, missing anchors, and schema errors surface immediately.
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
blume dev
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
</Step>
|
|
99
|
+
<Step title="Delete leftovers">
|
|
100
|
+
Old framework files (lockfile entries, `astro.config`, `theme.config`,
|
|
101
|
+
framework dependencies) are left untouched so nothing is lost. Remove them
|
|
102
|
+
by hand once you've confirmed the site builds.
|
|
103
|
+
</Step>
|
|
104
|
+
</Steps>
|
|
105
|
+
|
|
106
|
+
<CardGroup cols={2}>
|
|
107
|
+
<Card title="Bridge mode" href="/docs/advanced/bridge" icon="cable">
|
|
108
|
+
Serve a Mintlify project with `blume dev` — no migration, no file changes.
|
|
109
|
+
</Card>
|
|
110
|
+
<Card title="CLI reference" href="/docs/reference/cli" icon="terminal">
|
|
111
|
+
Every command and flag, including `blume migrate`.
|
|
112
|
+
</Card>
|
|
113
|
+
<Card title="Content sources" href="/docs/content/sources" icon="folder-tree">
|
|
114
|
+
How Blume scans content, assets, and remote sources into a site.
|
|
115
|
+
</Card>
|
|
116
|
+
<Card title="Frontmatter" href="/docs/reference/frontmatter" icon="file-text">
|
|
117
|
+
The page schema your migrated frontmatter is mapped onto.
|
|
118
|
+
</Card>
|
|
119
|
+
</CardGroup>
|
|
@@ -187,7 +187,7 @@ content: {
|
|
|
187
187
|
}
|
|
188
188
|
```
|
|
189
189
|
|
|
190
|
-
This is mainly useful after migrating from a tool that serves assets from the project root (like Mintlify), so references such as `` keep resolving without relocating every file.
|
|
190
|
+
This is mainly useful after [migrating](/docs/advanced/migrate) from a tool that serves assets from the project root (like Mintlify), so references such as `` keep resolving without relocating every file. [Bridge mode](/docs/advanced/bridge) uses the same mechanism to serve a Mintlify project's assets in place, untouched.
|
|
191
191
|
|
|
192
192
|
## Last modified
|
|
193
193
|
|
|
@@ -125,7 +125,7 @@ A negative or breaking state, such as a deprecation.
|
|
|
125
125
|
|
|
126
126
|
## Icon
|
|
127
127
|
|
|
128
|
-
Render an icon
|
|
128
|
+
Render an icon by name — the same `icon` props power cards, steps, tiles, tabs, and sidebar entries. Names come from three bundled libraries: [Lucide](https://lucide.dev) (the default), [Font Awesome](https://fontawesome.com/icons) (its free set), and [Tabler](https://tabler.io/icons). Names are lowercase and kebab-cased (`rocket`, `gauge-high`, `book-open`).
|
|
129
129
|
|
|
130
130
|
<Icon icon="rocket" size={20} />
|
|
131
131
|
|
|
@@ -133,7 +133,26 @@ Render an icon from Blume's built-in set by name — the same set the `icon` pro
|
|
|
133
133
|
<Icon icon="rocket" size={20} />
|
|
134
134
|
```
|
|
135
135
|
|
|
136
|
-
|
|
136
|
+
A bare name resolves against your project's [`icons.library`](#default-library) (Lucide unless you change it). Two ways to reach another library for a single icon:
|
|
137
|
+
|
|
138
|
+
- **`iconType`** selects a Font Awesome style — `solid` (default), `regular`, or `brands`: `<Icon icon="github" iconType="brands" />`. Font Awesome Pro styles (`light`, `thin`, `duotone`, `sharp-solid`) aren't in the bundled free data and fall back to solid.
|
|
139
|
+
- **A `library:name` prefix** overrides the default per icon: `fa6-solid:gauge-high`, `fa6-brands:github`, `lucide:rocket`, `tabler:heart`.
|
|
140
|
+
|
|
141
|
+
`size` sets the pixel size (default `16`) and `color` tints it (any CSS color; defaults to `currentColor`). Pass a raw `<svg>` string, an image URL, or a local image path in place of a name to render your own art, and add a `label` to expose it to assistive tech — without one, the icon is decorative.
|
|
142
|
+
|
|
143
|
+
Icons resolve at build time and inline as zero-JS SVG — nothing is fetched at runtime.
|
|
144
|
+
|
|
145
|
+
### Default library
|
|
146
|
+
|
|
147
|
+
Set which library bare icon names resolve against:
|
|
148
|
+
|
|
149
|
+
```ts title="blume.config.ts"
|
|
150
|
+
export default defineConfig({
|
|
151
|
+
icons: { library: "fontawesome" }, // "lucide" (default) | "fontawesome" | "tabler"
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
A `library:name` prefix always wins over this, so you can mix libraries on any default. Migrating from Mintlify sets this to `fontawesome` for you (Mintlify's default), so your existing icon names keep working.
|
|
137
156
|
|
|
138
157
|
## File tree
|
|
139
158
|
|
|
@@ -438,6 +457,40 @@ Show or hide content by audience. `for="web"` renders only on the site; `for="ag
|
|
|
438
457
|
<Visibility for="agents">Shown only in the generated Markdown.</Visibility>
|
|
439
458
|
```
|
|
440
459
|
|
|
460
|
+
## API fields
|
|
461
|
+
|
|
462
|
+
Document a single request/response field — a CLI flag, an SDK argument, an endpoint parameter — with its name, type, and description. `ParamField`, `ResponseField`, and `RequestField` are Mintlify-compatible, so a migrated site's field markup renders unchanged. Each takes a `type`, plus `required`, `deprecated`, and `default`; the description is the element's body and may hold rich MDX, including a nested [Expandable](#expandable) for sub-properties.
|
|
463
|
+
|
|
464
|
+
`ParamField` names the field through the attribute that marks its location — `path`, `query`, `header`, or `body` (or a plain `name`); the location shows as a small label.
|
|
465
|
+
|
|
466
|
+
<ParamField path="userId" type="string" required>
|
|
467
|
+
The unique identifier of the user.
|
|
468
|
+
</ParamField>
|
|
469
|
+
|
|
470
|
+
<ParamField query="limit" type="integer" default="20">
|
|
471
|
+
Maximum number of results to return per page.
|
|
472
|
+
</ParamField>
|
|
473
|
+
|
|
474
|
+
<ResponseField name="createdAt" type="string">
|
|
475
|
+
ISO 8601 timestamp for when the record was created.
|
|
476
|
+
</ResponseField>
|
|
477
|
+
|
|
478
|
+
```mdx
|
|
479
|
+
<ParamField path="userId" type="string" required>
|
|
480
|
+
The unique identifier of the user.
|
|
481
|
+
</ParamField>
|
|
482
|
+
|
|
483
|
+
<ParamField query="limit" type="integer" default="20">
|
|
484
|
+
Maximum number of results to return per page.
|
|
485
|
+
</ParamField>
|
|
486
|
+
|
|
487
|
+
<ResponseField name="createdAt" type="string">
|
|
488
|
+
ISO 8601 timestamp for when the record was created.
|
|
489
|
+
</ResponseField>
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
For a full spec-driven API reference — one page per operation with generated schemas — use the [OpenAPI reference](/docs/advanced/api-reference) instead.
|
|
493
|
+
|
|
441
494
|
## Type tables
|
|
442
495
|
|
|
443
496
|
Tables for documenting an object's properties — its props, types, and defaults. Write the rows by hand with `TypeTable`, or generate them straight from a TypeScript interface or type alias with `AutoTypeTable`.
|
package/docs/content/i18n.mdx
CHANGED
|
@@ -22,7 +22,7 @@ i18n: {
|
|
|
22
22
|
|
|
23
23
|
Each locale has a `code` (used in URLs), a `label` (shown in the language switcher), and an optional `dir` for right-to-left scripts (`"ltr"` by default).
|
|
24
24
|
|
|
25
|
-
:::tip Migrating from Mintlify? `blume migrate mintlify` reads `navigation.languages[]` and writes the `i18n` block for you — the `default: true` language becomes `defaultLocale`, and translated folders already match Blume's layout. :::
|
|
25
|
+
:::tip Migrating from Mintlify? [`blume migrate mintlify`](/docs/advanced/migrate) reads `navigation.languages[]` and writes the `i18n` block for you — the `default: true` language becomes `defaultLocale`, and translated folders already match Blume's layout. :::
|
|
26
26
|
|
|
27
27
|
## Organize translated content
|
|
28
28
|
|
package/docs/content/syntax.mdx
CHANGED
|
@@ -338,10 +338,10 @@ Call out a destructive or breaking action that can't easily be undone.
|
|
|
338
338
|
|
|
339
339
|
An informational aside; an alias-friendly default that reads as neutral.
|
|
340
340
|
|
|
341
|
-
:::info The core theme ships
|
|
341
|
+
:::info The core theme ships no client framework JS. :::
|
|
342
342
|
|
|
343
343
|
```md
|
|
344
|
-
:::info The core theme ships
|
|
344
|
+
:::info The core theme ships no client framework JS. :::
|
|
345
345
|
```
|
|
346
346
|
|
|
347
347
|
The names `caution`, `error`, `important`, and `warn` are accepted as aliases for `warning`, `danger`, `note`, and `warning` respectively.
|
package/docs/index.mdx
CHANGED
|
@@ -27,7 +27,7 @@ Blume takes the best of both worlds. The framework is the template, so the only
|
|
|
27
27
|
|
|
28
28
|
### Fast by default
|
|
29
29
|
|
|
30
|
-
Blume builds on Astro and Vite and renders static HTML by default — fast, cacheable, and cheap to host. The core theme
|
|
30
|
+
Blume builds on Astro and Vite and renders static HTML by default — fast, cacheable, and cheap to host. The core theme ships no client framework JS so pages score well on Core Web Vitals out of the box. Dev startup and hot reload feel Vite-native, and you opt into server features only when you need them.
|
|
31
31
|
|
|
32
32
|
### AI-ready out of the box
|
|
33
33
|
|
|
@@ -49,7 +49,7 @@ Your [`blume.config.ts`](/docs/configuration) and every [`meta.ts`](/docs/conten
|
|
|
49
49
|
- **Navigation** — inferred from files, refined with `meta.ts` or config.
|
|
50
50
|
- **SEO** — metadata, Open Graph images, RSS feeds, and JSON-LD, [built in](/docs/configuration/seo).
|
|
51
51
|
- **Customization** — component overrides, React islands, custom pages, theme tokens, and a source-component registry via `blume add`.
|
|
52
|
-
- **Migration** — `blume migrate mintlify | starlight | nextra | fumadocs
|
|
52
|
+
- **Migration** — [`blume migrate mintlify | starlight | nextra | fumadocs`](/docs/advanced/migrate), or run `blume dev` on a Mintlify project as-is with [bridge mode](/docs/advanced/bridge).
|
|
53
53
|
- **Eject** — `blume eject` produces a standalone Astro project that still uses the `blume` package.
|
|
54
54
|
|
|
55
55
|
## How it works
|
package/docs/reference/cli.mdx
CHANGED
|
@@ -16,7 +16,7 @@ blume <command> [options]
|
|
|
16
16
|
| `blume build` | Build the static (or server) site. |
|
|
17
17
|
| `blume preview` | Preview the last build. |
|
|
18
18
|
| `blume add <item>` | Install a source component from the registry. |
|
|
19
|
-
| `blume migrate <tool>` | Migrate from Mintlify, Starlight, Nextra, or Fumadocs. |
|
|
19
|
+
| [`blume migrate <tool>`](/docs/advanced/migrate) | Migrate from Mintlify, Starlight, Nextra, or Fumadocs. |
|
|
20
20
|
| `blume sync` | Re-fetch remote content sources and regenerate. |
|
|
21
21
|
| `blume eject` | Promote the runtime into a standalone Astro app. |
|
|
22
22
|
| `blume check` | Type-check the site with `astro check`. |
|
|
@@ -37,16 +37,44 @@ blume <command> [options]
|
|
|
37
37
|
- `blume build --output static|server --adapter vercel|node|netlify|cloudflare --base /docs` — override the deployment output, adapter, and base path from `blume.config.ts`.
|
|
38
38
|
- `blume build --analyze` — print the client JavaScript bundle sizes (largest first) after the build.
|
|
39
39
|
- `blume build --budget-js <kb> --budget-css <kb>` — fail the build when total client JavaScript/CSS exceeds the budget, turning a performance target into a CI gate.
|
|
40
|
+
- `blume build --isolated` — build into a throwaway `.blume-verify/` runtime (and its own `dist/`) instead of `.blume/`, so a running `blume dev` server and your real `dist/` are left untouched. See [Verifying while the dev server runs](#verifying-while-the-dev-server-runs).
|
|
40
41
|
- `blume preview --host --port <n>` — bind the preview server.
|
|
41
42
|
- `blume sync --force` — re-fetch remote sources, dropping the cached snapshot first.
|
|
42
43
|
- `blume add <item> --force` — overwrite files that already exist.
|
|
43
44
|
- `blume check --preview` — include drafts and unpublished CMS content when checking.
|
|
44
45
|
- `blume check --strict` — fail on content diagnostics as well as type errors.
|
|
46
|
+
- `blume check --isolated` — type-check in a throwaway `.blume-verify/` runtime so a running `blume dev` server is left untouched. See [Verifying while the dev server runs](#verifying-while-the-dev-server-runs).
|
|
45
47
|
- `blume eject --yes` — skip the confirmation prompt.
|
|
46
48
|
- `blume validate --external` — also check external links over the network.
|
|
47
49
|
- `blume validate --strict` — exit non-zero on warnings too.
|
|
48
50
|
- `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.
|
|
49
51
|
|
|
52
|
+
## Verifying while the dev server runs
|
|
53
|
+
|
|
54
|
+
`blume dev` serves a live Astro server rooted at the generated `.blume/` runtime and regenerates it on every change. `blume build` and `blume check` regenerate the _same_ `.blume/`, so running either while the dev server is live would corrupt it — both refuse with an error and exit non-zero:
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
A `blume dev` server is running against .blume; building would corrupt it.
|
|
58
|
+
Stop the dev server, or re-run with --isolated to build/verify against
|
|
59
|
+
.blume-verify without touching it.
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The `--isolated` flag is the escape hatch. It relocates the entire generated runtime (and, for `build`, its output `dist/`) to a sibling `.blume-verify/` directory, so the verification never writes anything the dev server — or your real `dist/` — depends on:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# In a second terminal, while `blume dev` is running:
|
|
66
|
+
blume check --isolated # fast: type-check the .astro/config changes
|
|
67
|
+
blume build --isolated # thorough: full production render into .blume-verify/dist
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
`check --isolated` is the quick path (Astro type + template diagnostics, no `dist/`); `build --isolated` is the heavier one that also catches runtime render errors. Isolated builds skip the deploy post-steps (search index, hosted-provider sync, `llms.txt`, sitemap/robots, redirects) — a verify only needs to confirm the site compiles and renders, not publish it. Blume adds `.blume-verify/` to your `.gitignore` automatically.
|
|
71
|
+
|
|
72
|
+
This is especially useful when a coding agent needs to verify changes while you keep the dev server open. To make plain `blume build`/`blume check` isolate without the flag — for example in an agent's shell — set `BLUME_RUNTIME_DIR` to the runtime directory to use:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
export BLUME_RUNTIME_DIR=.blume-verify
|
|
76
|
+
```
|
|
77
|
+
|
|
50
78
|
## Type-checking
|
|
51
79
|
|
|
52
80
|
`blume check` runs [`astro check`](https://docs.astro.build/en/reference/cli-reference/#astro-check) over your project. It regenerates the `.blume` runtime, syncs Astro's content types, then reports any TypeScript errors — in your `blume.config.ts`, in custom `.astro` pages, and in the components they import. It exits non-zero when there are errors, so it works as a `typecheck` step in CI:
|
|
@@ -18,6 +18,11 @@ Every page accepts the following frontmatter. All fields are optional.
|
|
|
18
18
|
type: "string",
|
|
19
19
|
description: "Publish date for blog/changelog feeds (ISO or YAML date).",
|
|
20
20
|
},
|
|
21
|
+
authors: {
|
|
22
|
+
type: "string | string[] | object[]",
|
|
23
|
+
description:
|
|
24
|
+
"Post author(s) for blog/changelog content — a name, or objects with a name plus optional avatar/url and any extra fields. Preserved as-is.",
|
|
25
|
+
},
|
|
21
26
|
slug: { type: "string", description: "Override the generated slug." },
|
|
22
27
|
draft: {
|
|
23
28
|
type: "boolean",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blume",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Documentation that's fast, AI-ready, and zero-config.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"astro",
|
|
@@ -50,6 +50,7 @@
|
|
|
50
50
|
"./components": "./src/components/index.ts",
|
|
51
51
|
"./components/*": "./src/components/*",
|
|
52
52
|
"./core/*": "./src/core/*",
|
|
53
|
+
"./openapi/*": "./src/openapi/*",
|
|
53
54
|
"./sources/*": "./src/core/sources/*",
|
|
54
55
|
"./theme/*": "./src/theme/*",
|
|
55
56
|
"./package.json": "./package.json"
|
|
@@ -69,10 +70,19 @@
|
|
|
69
70
|
"@astrojs/node": "^11.0.0",
|
|
70
71
|
"@astrojs/react": "^6.0.0",
|
|
71
72
|
"@astrojs/vercel": "^11.0.0",
|
|
73
|
+
"@iconify-json/fa6-brands": "^1.2.6",
|
|
74
|
+
"@iconify-json/fa6-regular": "^1.2.4",
|
|
75
|
+
"@iconify-json/fa6-solid": "^1.2.4",
|
|
76
|
+
"@iconify-json/lucide": "^1.2.115",
|
|
77
|
+
"@iconify-json/tabler": "^1.2.35",
|
|
78
|
+
"@iconify/types": "^2.0.0",
|
|
79
|
+
"@iconify/utils": "^3.1.3",
|
|
72
80
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
73
81
|
"@orama/orama": "^3.1.18",
|
|
74
82
|
"@pierre/diffs": "^1.2.11",
|
|
75
83
|
"@scalar/astro": "^0.4.5",
|
|
84
|
+
"@scalar/openapi-parser": "^0.28.8",
|
|
85
|
+
"@scalar/openapi-types": "^0.9.1",
|
|
76
86
|
"@shikijs/transformers": "^4.2.0",
|
|
77
87
|
"@shikijs/twoslash": "^4.2.0",
|
|
78
88
|
"@tailwindcss/typography": "^0.5.20",
|
package/src/astro/generate.ts
CHANGED
|
@@ -36,11 +36,9 @@ import type { ResolvedConfig } from "../core/schema.ts";
|
|
|
36
36
|
import { resolveTsconfigAliases } from "../core/tsconfig-aliases.ts";
|
|
37
37
|
import type { Navigation } from "../core/types.ts";
|
|
38
38
|
import { buildRssFeeds, renderRssFeed } from "../deploy/rss.ts";
|
|
39
|
-
import {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
referenceTabs,
|
|
43
|
-
} from "../openapi/scalar.ts";
|
|
39
|
+
import { hasScalarReferences, referenceTabs } from "../openapi/references.ts";
|
|
40
|
+
import { buildReferenceFiles } from "../openapi/scalar.ts";
|
|
41
|
+
import { isOpenApiSource } from "../openapi/source.ts";
|
|
44
42
|
import { registry } from "../registry/registry.ts";
|
|
45
43
|
import { buildSearchDocuments } from "../search/documents.ts";
|
|
46
44
|
import { searchProviderMeta, servesStaticIndex } from "../search/providers.ts";
|
|
@@ -582,8 +580,9 @@ export const buildRuntimeData = (project: BlumeProject): string => {
|
|
|
582
580
|
|
|
583
581
|
const { i18n } = config;
|
|
584
582
|
|
|
585
|
-
// API reference routes
|
|
586
|
-
//
|
|
583
|
+
// API reference routes surface as header tabs alongside the content-derived
|
|
584
|
+
// ones (Blume-rendered references also own a tab-scoped sidebar of operations),
|
|
585
|
+
// so the reference stays discoverable in every locale.
|
|
587
586
|
const withReferenceTabs = (nav: Navigation): Navigation => ({
|
|
588
587
|
...nav,
|
|
589
588
|
repoUrl: config.navigation.repo && repoUrl ? repoUrl : null,
|
|
@@ -650,6 +649,7 @@ export const buildRuntimeData = (project: BlumeProject): string => {
|
|
|
650
649
|
})),
|
|
651
650
|
}
|
|
652
651
|
: null,
|
|
652
|
+
icons: config.icons,
|
|
653
653
|
imageZoom: config.markdown.imageZoom,
|
|
654
654
|
logo: resolveLogo(project),
|
|
655
655
|
mcp: config.mcp.enabled
|
|
@@ -912,6 +912,7 @@ export const generateRuntime = async (
|
|
|
912
912
|
const themePath = join(srcDir, "generated", "app.css");
|
|
913
913
|
const searchClientPath = join(srcDir, "generated", "search-client.ts");
|
|
914
914
|
const examplesPath = join(srcDir, "generated", "examples.ts");
|
|
915
|
+
const openapiPath = join(srcDir, "generated", "openapi.json");
|
|
915
916
|
|
|
916
917
|
// Record every file this pass writes so orphans (from a now-disabled feature)
|
|
917
918
|
// can be pruned afterwards. `write` wraps the atomic writer and tracks paths.
|
|
@@ -987,6 +988,7 @@ export const generateRuntime = async (
|
|
|
987
988
|
needsReact,
|
|
988
989
|
needsSvelte,
|
|
989
990
|
needsVue,
|
|
991
|
+
openapiPath,
|
|
990
992
|
pages,
|
|
991
993
|
searchClientPath,
|
|
992
994
|
themePath,
|
|
@@ -1235,7 +1237,7 @@ export const generateRuntime = async (
|
|
|
1235
1237
|
// React ships with Blume; Vue/Svelte islands need their Astro integration
|
|
1236
1238
|
// installed by the project. Warn early rather than let Vite fail to resolve it.
|
|
1237
1239
|
warnings.push(...islandFrameworkWarnings(frameworks, context.root));
|
|
1238
|
-
if (
|
|
1240
|
+
if (hasScalarReferences(config)) {
|
|
1239
1241
|
const references = await buildReferenceFiles({
|
|
1240
1242
|
config,
|
|
1241
1243
|
contentRoutes: new Set(project.graph.pages.map((page) => page.route)),
|
|
@@ -1254,6 +1256,14 @@ export const generateRuntime = async (
|
|
|
1254
1256
|
join(srcDir, "generated", "data.json"),
|
|
1255
1257
|
buildRuntimeData(project)
|
|
1256
1258
|
);
|
|
1259
|
+
// The parsed OpenAPI specs behind the `blume:openapi` alias. Always written
|
|
1260
|
+
// (even as `{}`) so the alias resolves whether or not a reference is enabled;
|
|
1261
|
+
// the source parsed the specs during the scan, so this is just serialization.
|
|
1262
|
+
const openApiSource = project.sources.find(isOpenApiSource);
|
|
1263
|
+
await write(
|
|
1264
|
+
openapiPath,
|
|
1265
|
+
`${JSON.stringify(openApiSource ? openApiSource.openApiData() : {})}\n`
|
|
1266
|
+
);
|
|
1257
1267
|
await write(
|
|
1258
1268
|
join(out, "blume.manifest.json"),
|
|
1259
1269
|
`${JSON.stringify(project.manifest, null, 2)}\n`
|
package/src/astro/templates.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type { AskBackend } from "../ai/ask.ts";
|
|
|
7
7
|
import { resolveAssetMounts } from "../core/assets.ts";
|
|
8
8
|
import type { ResolvedConfig } from "../core/schema.ts";
|
|
9
9
|
import type { ProjectContext } from "../core/types.ts";
|
|
10
|
+
import { hasScalarReferences } from "../openapi/references.ts";
|
|
10
11
|
import { searchProviderMeta } from "../search/providers.ts";
|
|
11
12
|
import { buildFontEntries } from "../theme/fonts.ts";
|
|
12
13
|
import type { ExampleSpec } from "./examples.ts";
|
|
@@ -99,9 +100,10 @@ export const runtimeDependencies = (options: {
|
|
|
99
100
|
if (needsSvelte) {
|
|
100
101
|
deps.push("@astrojs/svelte");
|
|
101
102
|
}
|
|
102
|
-
// The Scalar integration is only declared
|
|
103
|
-
//
|
|
104
|
-
|
|
103
|
+
// The Scalar integration is only declared for a Scalar-rendered reference
|
|
104
|
+
// (the `renderer: "scalar"` fallback, or AsyncAPI). Blume-rendered OpenAPI
|
|
105
|
+
// parses at generate time and needs no runtime Scalar dependency.
|
|
106
|
+
if (hasScalarReferences(config)) {
|
|
105
107
|
deps.push("@scalar/astro");
|
|
106
108
|
}
|
|
107
109
|
// Only the configured search provider's SDK is declared, so a project pulls in
|
|
@@ -178,6 +180,10 @@ const renderUserAliases = (
|
|
|
178
180
|
)
|
|
179
181
|
.join("");
|
|
180
182
|
|
|
183
|
+
/** Astro's build output dir: the runtime's own `distDir`, else `<root>/dist`. */
|
|
184
|
+
const astroOutDir = (context: ProjectContext): string =>
|
|
185
|
+
context.distDir ?? `${context.root}/dist`;
|
|
186
|
+
|
|
181
187
|
export const astroConfigTemplate = (options: {
|
|
182
188
|
context: ProjectContext;
|
|
183
189
|
config: ResolvedConfig;
|
|
@@ -190,6 +196,7 @@ export const astroConfigTemplate = (options: {
|
|
|
190
196
|
examplesPath: string;
|
|
191
197
|
themePath: string;
|
|
192
198
|
searchClientPath: string;
|
|
199
|
+
openapiPath: string;
|
|
193
200
|
/** Project tsconfig path aliases (`find` -> absolute dir), e.g. `@` -> src. */
|
|
194
201
|
aliases?: Record<string, string>;
|
|
195
202
|
}): string => {
|
|
@@ -199,6 +206,7 @@ export const astroConfigTemplate = (options: {
|
|
|
199
206
|
examplesPath,
|
|
200
207
|
needsSvelte,
|
|
201
208
|
needsVue,
|
|
209
|
+
openapiPath,
|
|
202
210
|
searchClientPath,
|
|
203
211
|
} = options;
|
|
204
212
|
const { deployment } = config;
|
|
@@ -322,7 +330,7 @@ ${twoslashImport}${reactImport}${vueImport}${svelteImport}${blumeImport}${adapte
|
|
|
322
330
|
export default defineConfig({
|
|
323
331
|
root: ${JSON.stringify(context.outDir)},
|
|
324
332
|
srcDir: ${JSON.stringify(`${context.outDir}/src`)},
|
|
325
|
-
outDir: ${JSON.stringify(
|
|
333
|
+
outDir: ${JSON.stringify(astroOutDir(context))},
|
|
326
334
|
publicDir: ${JSON.stringify(`${context.root}/public`)},
|
|
327
335
|
output: ${JSON.stringify(deployment.output)},${adapterOption}${siteOption}${baseOption}${redirectsOption}${i18nOption}${fontsOption}
|
|
328
336
|
integrations: [${integrations.join(", ")}],
|
|
@@ -357,6 +365,7 @@ export default defineConfig({
|
|
|
357
365
|
alias: {
|
|
358
366
|
"blume:data": ${JSON.stringify(dataPath)},
|
|
359
367
|
"blume:examples": ${JSON.stringify(examplesPath)},
|
|
368
|
+
"blume:openapi": ${JSON.stringify(openapiPath)},
|
|
360
369
|
"blume:search-client": ${JSON.stringify(searchClientPath)},
|
|
361
370
|
"blume:theme": ${JSON.stringify(themePath)},${userAliasLines}
|
|
362
371
|
},
|
|
@@ -913,7 +922,10 @@ import FileTree from "blume/components/content/FileTree.astro";
|
|
|
913
922
|
import Frame from "blume/components/content/Frame.astro";
|
|
914
923
|
import GithubInfo from "blume/components/content/GithubInfo.astro";
|
|
915
924
|
import Panel from "blume/components/content/Panel.astro";
|
|
925
|
+
import ParamField from "blume/components/content/ParamField.astro";
|
|
916
926
|
import Prompt from "blume/components/content/Prompt.astro";
|
|
927
|
+
import RequestField from "blume/components/content/RequestField.astro";
|
|
928
|
+
import ResponseField from "blume/components/content/ResponseField.astro";
|
|
917
929
|
import Step from "blume/components/content/Step.astro";
|
|
918
930
|
import Steps from "blume/components/content/Steps.astro";
|
|
919
931
|
import Tab from "blume/components/content/Tab.astro";
|
|
@@ -927,6 +939,8 @@ import TypeTable from "blume/components/content/TypeTable.astro";
|
|
|
927
939
|
import Visibility from "blume/components/content/Visibility.astro";
|
|
928
940
|
import YouTube from "blume/components/content/YouTube.astro";
|
|
929
941
|
import Icon from "blume/components/Icon.astro";
|
|
942
|
+
import ApiOverview from "blume/components/openapi/ApiOverview.astro";
|
|
943
|
+
import Operation from "blume/components/openapi/Operation.astro";
|
|
930
944
|
${mathImport}import { mdxComponents as userMdx, layoutOverrides } from "../generated/components.ts";
|
|
931
945
|
import { islandComponents } from "../generated/islands.ts";
|
|
932
946
|
import data from "../generated/data.json";
|
|
@@ -943,6 +957,7 @@ export const prerender = true;
|
|
|
943
957
|
const components = {
|
|
944
958
|
Accordion,
|
|
945
959
|
AccordionItem,
|
|
960
|
+
ApiOverview,
|
|
946
961
|
AutoTypeTable,
|
|
947
962
|
Badge,
|
|
948
963
|
Callout,
|
|
@@ -960,8 +975,12 @@ const components = {
|
|
|
960
975
|
Frame,
|
|
961
976
|
GithubInfo,
|
|
962
977
|
Icon,
|
|
978
|
+
Operation,
|
|
963
979
|
Panel,
|
|
980
|
+
ParamField,
|
|
964
981
|
Prompt,
|
|
982
|
+
RequestField,
|
|
983
|
+
ResponseField,
|
|
965
984
|
Step,
|
|
966
985
|
Steps,
|
|
967
986
|
Tab,
|
|
@@ -1448,6 +1467,11 @@ declare module "blume:data" {
|
|
|
1448
1467
|
export default data;
|
|
1449
1468
|
}
|
|
1450
1469
|
|
|
1470
|
+
declare module "blume:openapi" {
|
|
1471
|
+
const specs: import("blume/openapi/model.ts").OpenApiData;
|
|
1472
|
+
export default specs;
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1451
1475
|
declare module "blume:search-client" {
|
|
1452
1476
|
export const createSearch: () =>
|
|
1453
1477
|
| import("blume/components/layout/search/types.ts").SearchFn
|