blume 0.5.3 → 0.5.4
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 +2985 -6883
- package/dist/cli/index.js.map +27 -58
- package/dist/types/core/config.d.ts +0 -8
- package/dist/types/core/data.d.ts +2 -2
- package/dist/types/core/schema.d.ts +58 -453
- package/dist/types/core/types.d.ts +1 -7
- package/docs/advanced/meta.ts +1 -8
- package/docs/advanced/skills.mdx +28 -0
- package/docs/configuration/ai.mdx +2 -0
- package/docs/configuration/index.mdx +13 -17
- package/docs/configuration/theming.mdx +2 -10
- package/docs/content/components.mdx +2 -53
- package/docs/content/i18n.mdx +0 -4
- package/docs/content/syntax.mdx +1 -1
- package/docs/index.mdx +0 -1
- package/docs/reference/cli.mdx +12 -13
- package/package.json +4 -6
- package/skills/blume/SKILL.md +71 -0
- package/skills/blume-update-docs/SKILL.md +52 -0
- package/skills/blume-update-docs/references/audit-checklist.md +46 -0
- package/src/astro/generate.ts +15 -12
- package/src/astro/integration.ts +0 -21
- package/src/astro/templates.ts +9 -16
- package/src/cli/commands/dev.ts +0 -6
- package/src/cli/commands/validate.ts +0 -2
- package/src/cli/index.ts +0 -2
- package/src/components/BlumePage.astro +0 -6
- package/src/components/Icon.astro +1 -12
- package/src/components/content/AccordionItem.astro +3 -6
- package/src/components/content/Badge.astro +1 -3
- package/src/components/content/Callout.astro +3 -9
- package/src/components/content/Card.astro +2 -3
- package/src/components/content/ColorItem.astro +2 -2
- package/src/components/content/Column.astro +1 -1
- package/src/components/content/Prompt.astro +1 -1
- package/src/components/content/Step.astro +3 -4
- package/src/components/content/Tab.astro +2 -3
- package/src/components/content/TypeTable.astro +5 -5
- package/src/components/content/Update.astro +1 -1
- package/src/components/layout/Header.astro +26 -3
- package/src/components/layout/Logo.astro +5 -1
- package/src/components/layout/NavSelector.astro +1 -1
- package/src/components/layout/NavTree.astro +14 -14
- package/src/components/layout/PageLayout.astro +42 -0
- package/src/components/layout/ReferenceLayout.astro +1 -0
- package/src/components/layout/RootLayout.astro +31 -2
- package/src/components/layout/Search.astro +5 -5
- package/src/components/layout/nav-utils.ts +9 -4
- package/src/components/openapi/ApiOverview.astro +4 -50
- package/src/components/openapi/ApiTagOperations.astro +42 -0
- package/src/core/builtin-tags.ts +1 -3
- package/src/core/config.ts +5 -28
- package/src/core/data.ts +2 -2
- package/src/core/graph.ts +1 -4
- package/src/core/links.ts +5 -19
- package/src/core/meta.ts +1 -1
- package/src/core/navigation.ts +0 -5
- package/src/core/project-graph.ts +0 -5
- package/src/core/schema.ts +13 -90
- package/src/core/sources/resolve.ts +0 -13
- package/src/core/sources/watch.ts +3 -2
- package/src/core/types.ts +1 -8
- package/src/openapi/parse.ts +197 -14
- package/src/openapi/render-mdx.ts +44 -10
- package/src/openapi/source.ts +19 -2
- package/src/theme/entry.ts +45 -17
- package/src/theme/icons.ts +18 -109
- package/src/theme/palette.ts +4 -37
- package/src/theme/twoslash.ts +6 -1
- package/dist/types/core/bridge.d.ts +0 -24
- package/dist/types/core/package-json.d.ts +0 -12
- package/dist/types/migrate/mintlify/assets.d.ts +0 -8
- package/dist/types/migrate/mintlify/config.d.ts +0 -16
- package/dist/types/migrate/mintlify/i18n.d.ts +0 -7
- package/dist/types/migrate/shared.d.ts +0 -153
- package/docs/advanced/bridge.mdx +0 -76
- package/docs/advanced/migrate.mdx +0 -124
- package/src/astro/static-assets.ts +0 -124
- package/src/cli/commands/migrate.ts +0 -39
- package/src/components/content/ApiField.astro +0 -75
- package/src/components/content/ParamField.astro +0 -39
- package/src/components/content/RequestField.astro +0 -23
- package/src/components/content/ResponseField.astro +0 -23
- package/src/components/content/Warning.astro +0 -9
- package/src/core/assets.ts +0 -31
- package/src/core/bridge.ts +0 -102
- package/src/core/sources/mintlify.ts +0 -190
- package/src/migrate/fumadocs/config.ts +0 -155
- package/src/migrate/fumadocs/content.ts +0 -376
- package/src/migrate/fumadocs/frontmatter.ts +0 -18
- package/src/migrate/fumadocs/groups.ts +0 -237
- package/src/migrate/fumadocs/index.ts +0 -355
- package/src/migrate/fumadocs/meta.ts +0 -244
- package/src/migrate/migrate.ts +0 -53
- package/src/migrate/mintlify/assets.ts +0 -46
- package/src/migrate/mintlify/config.ts +0 -954
- package/src/migrate/mintlify/content.ts +0 -120
- package/src/migrate/mintlify/frontmatter.ts +0 -126
- package/src/migrate/mintlify/i18n.ts +0 -51
- package/src/migrate/mintlify/icons.ts +0 -128
- package/src/migrate/mintlify/index.ts +0 -459
- package/src/migrate/mintlify/snippets.ts +0 -315
- package/src/migrate/mintlify/transform.ts +0 -82
- package/src/migrate/nextra/content.ts +0 -46
- package/src/migrate/nextra/frontmatter.ts +0 -40
- package/src/migrate/nextra/index.ts +0 -389
- package/src/migrate/nextra/meta.ts +0 -266
- package/src/migrate/shared.ts +0 -801
- package/src/migrate/starlight/config.ts +0 -455
- package/src/migrate/starlight/content.ts +0 -75
- package/src/migrate/starlight/frontmatter.ts +0 -111
- package/src/migrate/starlight/i18n.ts +0 -54
- package/src/migrate/starlight/index.ts +0 -131
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
import type { BlumeConfig } from "../core/schema.ts";
|
|
2
|
-
/**
|
|
3
|
-
* Whether `candidate` resolves to a path inside `root` (or is `root` itself).
|
|
4
|
-
* Guards migrators against `../` traversal in author-controlled source paths
|
|
5
|
-
* (`pages` entries, `<include>` targets) that would otherwise read or move
|
|
6
|
-
* files outside the docs tree.
|
|
7
|
-
*/
|
|
8
|
-
export declare const isInsideRoot: (root: string, candidate: string) => boolean;
|
|
9
|
-
/**
|
|
10
|
-
* Framework-agnostic helpers shared by more than one migrator. Each piece here
|
|
11
|
-
* was generalized from a migrator-specific implementation so Mintlify, Nextra,
|
|
12
|
-
* and future migrators converge on a single copy.
|
|
13
|
-
*/
|
|
14
|
-
/** Serialize a `BlumeConfig` to a `blume.config.ts` at the project root. */
|
|
15
|
-
export declare const writeBlumeConfig: (root: string, config: BlumeConfig) => Promise<void>;
|
|
16
|
-
/**
|
|
17
|
-
* Rewrite a migrated project's npm scripts off the old framework's CLI. A
|
|
18
|
-
* `dev`/`build`/`start` script whose command invokes `cli` (e.g. `/\bnext\b/`)
|
|
19
|
-
* is repointed at the matching Blume command (`start` -> `blume preview`); a
|
|
20
|
-
* script whose command matches `remove` (e.g. a `fumadocs-mdx` postinstall) is
|
|
21
|
-
* dropped. Scripts that don't match either are left untouched, so custom tasks
|
|
22
|
-
* survive. Returns true when `package.json` changed.
|
|
23
|
-
*/
|
|
24
|
-
export declare const rewriteFrameworkScripts: (root: string, cli: RegExp, remove?: RegExp) => Promise<boolean>;
|
|
25
|
-
/** Of the candidate project-relative paths, the ones that still exist — the old
|
|
26
|
-
* framework files a migration leaves behind for the user to remove by hand. */
|
|
27
|
-
export declare const leftoverFiles: (root: string, candidates: string[]) => string[];
|
|
28
|
-
/**
|
|
29
|
-
* Scaffold a minimal, runnable `package.json` when the migrated project has
|
|
30
|
-
* none. Config-only sources (e.g. a Mintlify `docs.json`) ship no npm manifest,
|
|
31
|
-
* so a fresh migration has nothing to run `blume dev` with; this writes a stub
|
|
32
|
-
* with `blume` as a dependency and `dev`/`build`/`doctor` scripts, making
|
|
33
|
-
* `npm install && npm run dev` work immediately. A pre-existing `package.json`
|
|
34
|
-
* is left untouched — {@link rewriteFrameworkScripts} repoints those instead.
|
|
35
|
-
* Returns true when a file was created.
|
|
36
|
-
*/
|
|
37
|
-
export declare const ensurePackageJson: (root: string) => Promise<boolean>;
|
|
38
|
-
/**
|
|
39
|
-
* Remove every match of an import pattern, collapsing the blank gap each
|
|
40
|
-
* removal leaves behind — without touching blank runs elsewhere in the
|
|
41
|
-
* document (double blank lines inside code fences are real content that a
|
|
42
|
-
* whole-document `\n{3,}` collapse used to corrupt).
|
|
43
|
-
*/
|
|
44
|
-
export declare const stripImports: (source: string, pattern: RegExp) => string;
|
|
45
|
-
export interface CalloutRewriteOptions {
|
|
46
|
-
/** Directive for a type-bearing tag with no `type` attribute (e.g. bare `<Callout>`). */
|
|
47
|
-
defaultDirective: string;
|
|
48
|
-
/** Tag names whose directive is fixed by the tag itself (e.g. `<Warning>`). */
|
|
49
|
-
tagDirectives: Record<string, string>;
|
|
50
|
-
/** Component tag names to convert. */
|
|
51
|
-
tags: string[];
|
|
52
|
-
/** `type="…"` values mapped to Blume directive names. */
|
|
53
|
-
typeDirectives: Record<string, string>;
|
|
54
|
-
}
|
|
55
|
-
/** Read a quoted string attribute (`name="…"` or `name='…'`) from a tag. */
|
|
56
|
-
export declare const attribute: (attrs: string, name: string) => string | undefined;
|
|
57
|
-
/**
|
|
58
|
-
* Find the `>` that closes an opening JSX tag, honoring quotes and `{…}`
|
|
59
|
-
* expression attributes (so a `>` inside `icon={"<svg…>"}` is not mistaken for
|
|
60
|
-
* the tag end). Returns -1 if unterminated.
|
|
61
|
-
*/
|
|
62
|
-
export declare const findOpenTagEnd: (source: string, from: number) => number;
|
|
63
|
-
/**
|
|
64
|
-
* Convert callout-style JSX components into Blume `:::` directives. Uses a
|
|
65
|
-
* quote/brace-aware tag scanner so callouts carrying JSX-expression attributes
|
|
66
|
-
* (e.g. inline-SVG icons) convert cleanly; non-convertible attributes (icons,
|
|
67
|
-
* colors, emoji) are dropped. A tag whose resolved directive is unknown is left
|
|
68
|
-
* untouched.
|
|
69
|
-
*/
|
|
70
|
-
export declare const rewriteCallouts: (source: string, options: CalloutRewriteOptions) => string;
|
|
71
|
-
/**
|
|
72
|
-
* Remove frontmatter keys Blume's strict page schema would reject (e.g. stray
|
|
73
|
-
* `og:*`/`twitter:*` metatags) so the migrated page validates, reporting what
|
|
74
|
-
* was dropped. Validation errors other than stray keys are left for `blume dev`
|
|
75
|
-
* to surface.
|
|
76
|
-
*/
|
|
77
|
-
export declare const stripUnknownPageMeta: (data: Record<string, unknown>) => {
|
|
78
|
-
data: Record<string, unknown>;
|
|
79
|
-
removed: string[];
|
|
80
|
-
};
|
|
81
|
-
/**
|
|
82
|
-
* Rename a JSX tag (open and close) while preserving its attributes. The
|
|
83
|
-
* trailing lookahead means a longer tag (e.g. `CardGrid`) is never matched by a
|
|
84
|
-
* rule for its shorter prefix (`Card`), so prefix-sharing renames can be chained
|
|
85
|
-
* — run the item-level rename before the container rename.
|
|
86
|
-
*/
|
|
87
|
-
export declare const renameTag: (source: string, from: string, to: string) => string;
|
|
88
|
-
/**
|
|
89
|
-
* Static readers for JS/TS config files (Nextra `_meta`, Starlight
|
|
90
|
-
* `astro.config`). Config is parsed by walking the source as text — quote-,
|
|
91
|
-
* comment-, and bracket-aware — rather than executing user code, matching the
|
|
92
|
-
* other migrators (which never eval). Values that aren't pure literals (an
|
|
93
|
-
* identifier, call, JSX, or interpolated template) are reported as `UNPARSEABLE`
|
|
94
|
-
* so the caller can drop the field and warn.
|
|
95
|
-
*/
|
|
96
|
-
/** Index of a string within a JS source: the close quote matching `s[open]`. */
|
|
97
|
-
export declare const findStringEnd: (s: string, open: number) => number;
|
|
98
|
-
export declare const unescapeString: (inner: string) => string;
|
|
99
|
-
export interface ObjectScanResult {
|
|
100
|
-
end: number;
|
|
101
|
-
entries: string[];
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Walk a `{…}` object literal starting at `openIndex`, returning the matching
|
|
105
|
-
* close-brace index and the raw `key: value` text of each top-level entry.
|
|
106
|
-
* Quote-, comment-, and bracket-aware so commas/braces nested in strings,
|
|
107
|
-
* arrays, or child objects don't split entries. Returns null if unterminated.
|
|
108
|
-
*/
|
|
109
|
-
export declare const scanObject: (source: string, openIndex: number) => ObjectScanResult | null;
|
|
110
|
-
export interface ArrayScanResult {
|
|
111
|
-
elements: string[];
|
|
112
|
-
end: number;
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* Walk a `[…]` array literal starting at `openIndex`, returning the matching
|
|
116
|
-
* close-bracket index and the raw text of each top-level element. The sibling of
|
|
117
|
-
* {@link scanObject}; a trailing comma yields no empty element.
|
|
118
|
-
*/
|
|
119
|
-
export declare const scanArray: (source: string, openIndex: number) => ArrayScanResult | null;
|
|
120
|
-
/**
|
|
121
|
-
* Strip `//` and block comments so they don't leak into entry text (the scanner
|
|
122
|
-
* splits on slices, so an inter-entry comment would otherwise attach to the next
|
|
123
|
-
* entry). String literals are preserved verbatim.
|
|
124
|
-
*/
|
|
125
|
-
export declare const stripJsComments: (source: string) => string;
|
|
126
|
-
export interface KeyValue {
|
|
127
|
-
key: string;
|
|
128
|
-
value: string;
|
|
129
|
-
}
|
|
130
|
-
/** Split a raw `key: value` entry at its top-level colon. */
|
|
131
|
-
export declare const splitKeyValue: (entry: string) => KeyValue | null;
|
|
132
|
-
/** Read an object key, unquoting it when it is a string literal. */
|
|
133
|
-
export declare const parseKey: (key: string) => string;
|
|
134
|
-
/** Read a clean string literal value, or null if it's an expression. */
|
|
135
|
-
export declare const readString: (value: string) => string | null;
|
|
136
|
-
/** A value that isn't a pure literal (identifier, call, JSX, computed, …). */
|
|
137
|
-
export declare const UNPARSEABLE: unique symbol;
|
|
138
|
-
export type LiteralValue = LiteralValue[] | boolean | null | number | string | typeof UNPARSEABLE | {
|
|
139
|
-
[key: string]: LiteralValue;
|
|
140
|
-
};
|
|
141
|
-
/**
|
|
142
|
-
* Evaluate a JS literal expression (string / number / boolean / null / array /
|
|
143
|
-
* object) into its value without executing it. Anything else resolves to
|
|
144
|
-
* {@link UNPARSEABLE}; inside arrays the sentinel keeps the element's position,
|
|
145
|
-
* inside objects it stays as the field's value so the caller can warn and drop.
|
|
146
|
-
*/
|
|
147
|
-
export declare const parseLiteral: (source: string) => LiteralValue;
|
|
148
|
-
/** Narrow a parsed literal to a string. */
|
|
149
|
-
export declare const asLiteralString: (value: LiteralValue | undefined) => string | undefined;
|
|
150
|
-
/** Narrow a parsed literal to a plain object (not an array or `UNPARSEABLE`). */
|
|
151
|
-
export declare const isLiteralObject: (value: LiteralValue | undefined) => value is Record<string, LiteralValue>;
|
|
152
|
-
/** Narrow a parsed literal to an array. */
|
|
153
|
-
export declare const asLiteralArray: (value: LiteralValue | undefined) => LiteralValue[] | undefined;
|
package/docs/advanced/bridge.mdx
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Bridge
|
|
3
|
-
description: Run blume dev directly on a Mintlify codebase — no config, no migration, no file changes. Blume detects docs.json and serves it in place.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
Bridge mode lets you point Blume at a **Mintlify** project and run it as-is. There's no config to write and no migration to commit: drop `blume dev` into a directory that has a `docs.json` (or legacy `mint.json`) and Blume detects it, synthesizes an equivalent config in memory, and serves your existing MDX — transformed to Blume idiom on the fly, on disk untouched.
|
|
7
|
-
|
|
8
|
-
```bash
|
|
9
|
-
cd my-mintlify-docs
|
|
10
|
-
npx blume dev
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
```txt
|
|
14
|
-
ℹ Detected docs.json — running in Mintlify bridge mode (no migration).
|
|
15
|
-
Run "blume migrate mintlify" to convert permanently.
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
It's the zero-commitment way to see what your docs look like on Blume — you can keep running `mintlify dev` in one terminal and `blume dev` in another, side by side, and decide later.
|
|
19
|
-
|
|
20
|
-
## When it activates
|
|
21
|
-
|
|
22
|
-
Bridge mode is entirely automatic, gated on two conditions:
|
|
23
|
-
|
|
24
|
-
- **No Blume config.** There's no `blume.config.{ts,js,mjs}` at the project root. An explicit Blume config always wins — the moment one exists, Blume runs a normal project and bridge mode never triggers.
|
|
25
|
-
- **A Mintlify config is present.** A `docs.json` or `mint.json` sits at the root.
|
|
26
|
-
|
|
27
|
-
With both true, Blume treats the Mintlify config as its source of truth for that run.
|
|
28
|
-
|
|
29
|
-
:::note
|
|
30
|
-
Bridge mode applies to `blume build` too, not just `blume dev` — you can produce a static site from an unconverted Mintlify project. Only `blume dev` prints the detection notice.
|
|
31
|
-
:::
|
|
32
|
-
|
|
33
|
-
## What happens under the hood
|
|
34
|
-
|
|
35
|
-
Bridge mode is the **read-only twin** of [`blume migrate mintlify`](/docs/advanced/migrate#mintlify): the same translation, without writing anything back.
|
|
36
|
-
|
|
37
|
-
- **Config is synthesized in memory.** `docs.json` is translated to a Blume config — navigation, theme, and chrome mapped across — exactly as the migrator would, but nothing is written to disk.
|
|
38
|
-
- **Content is served through a Mintlify source.** The config's content block is rewired to a single [`mintlify` content source](/docs/content/sources) rooted at the project. Each MDX page is transformed to Blume markup **at scan time** as it's read, so your files never change.
|
|
39
|
-
- **Variables are inlined at scan time.** `docs.json` `variables` (`{{name}}`) are substituted into content as it's scanned — Blume has no runtime substitution.
|
|
40
|
-
- **Assets are served in place.** Referenced folders like `images/` are served through [`content.assets`](/docs/content/sources) rather than moved to `public/`, so nothing is relocated.
|
|
41
|
-
- **Languages map to i18n.** A multi-language `docs.json` maps to Blume's [`i18n`](/docs/content/i18n), with the language switch handled by Blume's locale routing instead of a nav selector.
|
|
42
|
-
|
|
43
|
-
Because it reads the same `docs.json` and runs the same transforms, what you see in bridge mode is what you'd get from a full migration.
|
|
44
|
-
|
|
45
|
-
## Bridge vs. migrate
|
|
46
|
-
|
|
47
|
-
<CardGroup cols={2}>
|
|
48
|
-
<Card title="Bridge mode" icon="cable">
|
|
49
|
-
**Try Blume with zero changes.** Nothing is written; your Mintlify project
|
|
50
|
-
stays exactly as it is. Ideal for evaluating Blume, running both dev servers
|
|
51
|
-
side by side, or a reversible spike.
|
|
52
|
-
</Card>
|
|
53
|
-
<Card title="Migrate" href="/docs/advanced/migrate" icon="arrow-right">
|
|
54
|
-
**Convert for good.** [`blume migrate mintlify`](/docs/advanced/migrate)
|
|
55
|
-
rewrites your pages, config, and assets in place so Blume becomes the source
|
|
56
|
-
of truth. Do this once you've decided to switch.
|
|
57
|
-
</Card>
|
|
58
|
-
</CardGroup>
|
|
59
|
-
|
|
60
|
-
Everything you can do in bridge mode, you can do permanently by migrating — bridge is the preview, migrate is the commit.
|
|
61
|
-
|
|
62
|
-
## Limitations
|
|
63
|
-
|
|
64
|
-
- **Mintlify only.** Bridge detection is Mintlify-specific. The other frameworks — Fumadocs, Nextra, Starlight — need a [one-shot migration](/docs/advanced/migrate).
|
|
65
|
-
- **Same idiom gaps as the migrator.** Components without a Blume equivalent are transformed on a best-effort basis; when you hit one, [migrate](/docs/advanced/migrate) and address the warnings, or switch that page to a Blume-native equivalent such as the [OpenAPI reference](/docs/advanced/api-reference).
|
|
66
|
-
- **No runtime variables.** As with a migration, `{{variable}}` values are inlined at scan time rather than substituted at runtime.
|
|
67
|
-
|
|
68
|
-
<CardGroup cols={2}>
|
|
69
|
-
<Card title="Migrate" href="/docs/advanced/migrate" icon="arrow-right">
|
|
70
|
-
Convert a Mintlify, Fumadocs, Nextra, or Starlight project permanently.
|
|
71
|
-
</Card>
|
|
72
|
-
<Card title="Content sources" href="/docs/content/sources" icon="folder-tree">
|
|
73
|
-
How the `mintlify` source and `content.assets` fit into Blume's content
|
|
74
|
-
graph.
|
|
75
|
-
</Card>
|
|
76
|
-
</CardGroup>
|
|
@@ -1,124 +0,0 @@
|
|
|
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
|
|
22
|
-
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.
|
|
23
|
-
:::
|
|
24
|
-
|
|
25
|
-
## What a migration does
|
|
26
|
-
|
|
27
|
-
Every migrator runs the same shape of work, tuned to the source framework:
|
|
28
|
-
|
|
29
|
-
<Steps>
|
|
30
|
-
<Step title="Translate the config">
|
|
31
|
-
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.
|
|
32
|
-
</Step>
|
|
33
|
-
<Step title="Rewrite every page">
|
|
34
|
-
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.
|
|
35
|
-
</Step>
|
|
36
|
-
<Step title="Convert navigation">
|
|
37
|
-
Navigation files (`_meta.{js,ts,json}`, `meta.json`) become typed [`meta.ts`](/docs/content/meta) files, preserving order and titles.
|
|
38
|
-
</Step>
|
|
39
|
-
<Step title="Relocate assets and scripts">
|
|
40
|
-
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.
|
|
41
|
-
</Step>
|
|
42
|
-
</Steps>
|
|
43
|
-
|
|
44
|
-
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:
|
|
45
|
-
|
|
46
|
-
```txt
|
|
47
|
-
✔ Migrated 42 content file(s).
|
|
48
|
-
⚠ Components without a Blume equivalent need manual review: <Frame>.
|
|
49
|
-
⚠ Dropped unsupported page frontmatter keys: mode, "og:image".
|
|
50
|
-
▶ Review blume.config.ts and run `blume dev`.
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
## Tools
|
|
54
|
-
|
|
55
|
-
### Mintlify
|
|
56
|
-
|
|
57
|
-
Reads `docs.json` (or legacy `mint.json`) and rewrites every page **in place** — content stays at the project root (`content.root` is `"."`).
|
|
58
|
-
|
|
59
|
-
- Snippets under `/snippets` are inlined as includes; component snippets (e.g. `.jsx`) are kept and their imports rewritten to relative paths.
|
|
60
|
-
- 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.
|
|
61
|
-
- `docs.json` `variables` are inlined into content — Blume has no runtime `{{variable}}` substitution.
|
|
62
|
-
- The source `docs.json`/`mint.json` is removed once `blume.config.ts` is safely written. Leaving it around would keep the project a [Bridge mode](/docs/advanced/bridge) candidate — a later run without a Blume config would silently serve the un-migrated Mintlify site.
|
|
63
|
-
- Multi-language projects map to [`i18n.locales`](/docs/content/i18n); the language nav selector is dropped in favor of Blume's locale switching.
|
|
64
|
-
- 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.
|
|
65
|
-
- 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.
|
|
66
|
-
- 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.
|
|
67
|
-
|
|
68
|
-
:::tip
|
|
69
|
-
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.
|
|
70
|
-
:::
|
|
71
|
-
|
|
72
|
-
### Fumadocs
|
|
73
|
-
|
|
74
|
-
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`.
|
|
75
|
-
|
|
76
|
-
### Nextra
|
|
77
|
-
|
|
78
|
-
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.
|
|
79
|
-
|
|
80
|
-
### Starlight
|
|
81
|
-
|
|
82
|
-
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.
|
|
83
|
-
|
|
84
|
-
## After migrating
|
|
85
|
-
|
|
86
|
-
<Steps>
|
|
87
|
-
<Step title="Review the config">
|
|
88
|
-
Open `blume.config.ts` and check the mapped navigation, theme, and site
|
|
89
|
-
settings. The migrator is thorough but conservative — some source options
|
|
90
|
-
have no Blume equivalent and are left out.
|
|
91
|
-
</Step>
|
|
92
|
-
<Step title="Read the warnings">
|
|
93
|
-
Each warning points at something the codemod couldn't fully translate. Work
|
|
94
|
-
through them before shipping.
|
|
95
|
-
</Step>
|
|
96
|
-
<Step title="Run the dev server">
|
|
97
|
-
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.
|
|
98
|
-
|
|
99
|
-
```bash
|
|
100
|
-
blume dev
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
</Step>
|
|
104
|
-
<Step title="Delete leftovers">
|
|
105
|
-
Old framework files (lockfile entries, `astro.config`, `theme.config`,
|
|
106
|
-
framework dependencies) are left untouched so nothing is lost. Remove them
|
|
107
|
-
by hand once you've confirmed the site builds.
|
|
108
|
-
</Step>
|
|
109
|
-
</Steps>
|
|
110
|
-
|
|
111
|
-
<CardGroup cols={2}>
|
|
112
|
-
<Card title="Bridge mode" href="/docs/advanced/bridge" icon="cable">
|
|
113
|
-
Serve a Mintlify project with `blume dev` — no migration, no file changes.
|
|
114
|
-
</Card>
|
|
115
|
-
<Card title="CLI reference" href="/docs/reference/cli" icon="terminal">
|
|
116
|
-
Every command and flag, including `blume migrate`.
|
|
117
|
-
</Card>
|
|
118
|
-
<Card title="Content sources" href="/docs/content/sources" icon="folder-tree">
|
|
119
|
-
How Blume scans content, assets, and remote sources into a site.
|
|
120
|
-
</Card>
|
|
121
|
-
<Card title="Frontmatter" href="/docs/reference/frontmatter" icon="file-text">
|
|
122
|
-
The page schema your migrated frontmatter is mapped onto.
|
|
123
|
-
</Card>
|
|
124
|
-
</CardGroup>
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
import { createReadStream, existsSync, statSync } from "node:fs";
|
|
2
|
-
import type { Stats } from "node:fs";
|
|
3
|
-
import { cp } from "node:fs/promises";
|
|
4
|
-
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
5
|
-
|
|
6
|
-
import { extname, join, relative, resolve, sep } from "pathe";
|
|
7
|
-
|
|
8
|
-
import type { AssetMount } from "../core/assets.ts";
|
|
9
|
-
|
|
10
|
-
/** Content types for the asset extensions a docs project commonly serves. */
|
|
11
|
-
const MIME_TYPES: Record<string, string> = {
|
|
12
|
-
".avif": "image/avif",
|
|
13
|
-
".css": "text/css; charset=utf-8",
|
|
14
|
-
".gif": "image/gif",
|
|
15
|
-
".ico": "image/x-icon",
|
|
16
|
-
".jpeg": "image/jpeg",
|
|
17
|
-
".jpg": "image/jpeg",
|
|
18
|
-
".js": "text/javascript; charset=utf-8",
|
|
19
|
-
".json": "application/json; charset=utf-8",
|
|
20
|
-
".mp4": "video/mp4",
|
|
21
|
-
".otf": "font/otf",
|
|
22
|
-
".pdf": "application/pdf",
|
|
23
|
-
".png": "image/png",
|
|
24
|
-
".svg": "image/svg+xml",
|
|
25
|
-
".ttf": "font/ttf",
|
|
26
|
-
".txt": "text/plain; charset=utf-8",
|
|
27
|
-
".webm": "video/webm",
|
|
28
|
-
".webp": "image/webp",
|
|
29
|
-
".woff": "font/woff",
|
|
30
|
-
".woff2": "font/woff2",
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
const mimeType = (file: string): string =>
|
|
34
|
-
MIME_TYPES[extname(file).toLowerCase()] ?? "application/octet-stream";
|
|
35
|
-
|
|
36
|
-
/** Whether `child` is `parent` itself or a path nested under it. */
|
|
37
|
-
const isContained = (parent: string, child: string): boolean => {
|
|
38
|
-
const rel = relative(parent, child);
|
|
39
|
-
return rel === "" || (!rel.startsWith("..") && !rel.startsWith(sep));
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
/** Resolve a request URL to an on-disk file within one of the mounts, if any. */
|
|
43
|
-
const resolveRequest = (url: string, mounts: AssetMount[]): string | null => {
|
|
44
|
-
let pathname: string;
|
|
45
|
-
try {
|
|
46
|
-
pathname = decodeURIComponent(
|
|
47
|
-
(url.split("?")[0] ?? "").split("#")[0] ?? ""
|
|
48
|
-
);
|
|
49
|
-
} catch {
|
|
50
|
-
// Malformed percent-encoding (`/images/%zz`) throws URIError; treat it as
|
|
51
|
-
// a plain miss (404) rather than a middleware exception.
|
|
52
|
-
return null;
|
|
53
|
-
}
|
|
54
|
-
for (const mount of mounts) {
|
|
55
|
-
if (pathname !== mount.url && !pathname.startsWith(`${mount.url}/`)) {
|
|
56
|
-
continue;
|
|
57
|
-
}
|
|
58
|
-
// `.` + rel keeps the join relative so an absolute-looking suffix can't
|
|
59
|
-
// escape the mount; the containment check rejects `..` traversal outright.
|
|
60
|
-
const file = resolve(mount.dir, `.${pathname.slice(mount.url.length)}`);
|
|
61
|
-
if (isContained(mount.dir, file)) {
|
|
62
|
-
return file;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
return null;
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
/** `statSync` that returns null instead of throwing on a missing file. */
|
|
69
|
-
const statFile = (file: string): Stats | null => {
|
|
70
|
-
try {
|
|
71
|
-
return statSync(file);
|
|
72
|
-
} catch {
|
|
73
|
-
return null;
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* A dev-server middleware that serves `content.assets` mounts (top-level dirs
|
|
79
|
-
* kept in place, e.g. a migrated `images/`) at their site URL. Astro only serves
|
|
80
|
-
* `publicDir` in dev, so without this those references would 404. Non-matching
|
|
81
|
-
* requests, and any path that isn't a real file, fall through to Astro. Stat is
|
|
82
|
-
* synchronous — this is dev-only middleware and mirrors how sirv serves statics.
|
|
83
|
-
*/
|
|
84
|
-
export const serveAssetMounts =
|
|
85
|
-
(mounts: AssetMount[]) =>
|
|
86
|
-
(req: IncomingMessage, res: ServerResponse, next: () => void): void => {
|
|
87
|
-
if ((req.method !== "GET" && req.method !== "HEAD") || !req.url) {
|
|
88
|
-
next();
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
const file = resolveRequest(req.url, mounts);
|
|
92
|
-
const stats = file ? statFile(file) : null;
|
|
93
|
-
if (!(file && stats?.isFile())) {
|
|
94
|
-
next();
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
res.setHeader("Content-Type", mimeType(file));
|
|
98
|
-
res.setHeader("Content-Length", String(stats.size));
|
|
99
|
-
if (req.method === "HEAD") {
|
|
100
|
-
res.end();
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
createReadStream(file).pipe(res);
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Copy every asset mount into the build output, mirroring what Astro does with
|
|
108
|
-
* `publicDir`. Runs on `astro:build:done` so in-place asset dirs ship in the
|
|
109
|
-
* final `dist/`. A missing source dir is skipped (it may be referenced but
|
|
110
|
-
* absent); `cp` handles both directories and single files.
|
|
111
|
-
*/
|
|
112
|
-
export const copyAssetMounts = async (
|
|
113
|
-
mounts: AssetMount[],
|
|
114
|
-
outDir: string
|
|
115
|
-
): Promise<void> => {
|
|
116
|
-
for (const mount of mounts) {
|
|
117
|
-
if (!existsSync(mount.dir)) {
|
|
118
|
-
continue;
|
|
119
|
-
}
|
|
120
|
-
const dest = join(outDir, mount.url.replace(/^\/+/u, ""));
|
|
121
|
-
// oxlint-disable-next-line no-await-in-loop -- sequential fs copies
|
|
122
|
-
await cp(mount.dir, dest, { recursive: true });
|
|
123
|
-
}
|
|
124
|
-
};
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { defineCommand } from "citty";
|
|
2
|
-
|
|
3
|
-
import { migrators } from "../../migrate/migrate.ts";
|
|
4
|
-
import { logger } from "../log.ts";
|
|
5
|
-
|
|
6
|
-
const makeMigrateCommand = (source: keyof typeof migrators) =>
|
|
7
|
-
defineCommand({
|
|
8
|
-
meta: {
|
|
9
|
-
description: `Migrate a ${source} project to Blume.`,
|
|
10
|
-
name: source,
|
|
11
|
-
},
|
|
12
|
-
async run() {
|
|
13
|
-
const root = process.cwd();
|
|
14
|
-
logger.start(`Migrating ${source} project`);
|
|
15
|
-
const result = await migrators[source]?.(root);
|
|
16
|
-
if (!result) {
|
|
17
|
-
logger.error(`No migrator for ${source}.`);
|
|
18
|
-
process.exit(1);
|
|
19
|
-
}
|
|
20
|
-
logger.success(`Migrated ${result.moved} content file(s).`);
|
|
21
|
-
for (const warning of result.warnings) {
|
|
22
|
-
logger.warn(warning);
|
|
23
|
-
}
|
|
24
|
-
logger.box("Review blume.config.ts and run `blume dev`.");
|
|
25
|
-
},
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
export const migrateCommand = defineCommand({
|
|
29
|
-
meta: {
|
|
30
|
-
description: "Migrate from another docs tool to Blume.",
|
|
31
|
-
name: "migrate",
|
|
32
|
-
},
|
|
33
|
-
subCommands: {
|
|
34
|
-
fumadocs: makeMigrateCommand("fumadocs"),
|
|
35
|
-
mintlify: makeMigrateCommand("mintlify"),
|
|
36
|
-
nextra: makeMigrateCommand("nextra"),
|
|
37
|
-
starlight: makeMigrateCommand("starlight"),
|
|
38
|
-
},
|
|
39
|
-
});
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
// Shared field row behind the Mintlify-compat <ParamField>/<ResponseField>/
|
|
3
|
-
// <RequestField> components: a labeled name + type + badges, with the field's
|
|
4
|
-
// description rendered from the slot (which may hold rich MDX, including nested
|
|
5
|
-
// <Expandable> or further fields). Styled to match the native OpenAPI reference
|
|
6
|
-
// rows (src/components/openapi/), so a migrated site reads consistently.
|
|
7
|
-
interface Props {
|
|
8
|
-
default?: unknown;
|
|
9
|
-
deprecated?: unknown;
|
|
10
|
-
location?: string;
|
|
11
|
-
name?: string;
|
|
12
|
-
required?: unknown;
|
|
13
|
-
type?: string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const {
|
|
17
|
-
default: defaultValue,
|
|
18
|
-
deprecated,
|
|
19
|
-
location,
|
|
20
|
-
name,
|
|
21
|
-
required,
|
|
22
|
-
type,
|
|
23
|
-
} = Astro.props;
|
|
24
|
-
|
|
25
|
-
// Booleans arrive as MDX shorthand (`required`) or strings (`required="true"`).
|
|
26
|
-
const isTrue = (value: unknown): boolean => value === true || value === "true";
|
|
27
|
-
const hasDefault =
|
|
28
|
-
defaultValue !== undefined && defaultValue !== null && defaultValue !== "";
|
|
29
|
-
const hasDescription = Astro.slots.has("default");
|
|
30
|
-
---
|
|
31
|
-
|
|
32
|
-
<div class="not-prose my-2 rounded-blume border border-border px-4 py-3">
|
|
33
|
-
<div class="flex flex-wrap items-baseline gap-x-2 gap-y-1">
|
|
34
|
-
{name && <code class="font-mono text-foreground text-sm">{name}</code>}
|
|
35
|
-
{
|
|
36
|
-
location && (
|
|
37
|
-
<span class="rounded bg-muted px-1.5 py-0.5 font-medium text-[0.625rem] text-muted-foreground uppercase tracking-wide">
|
|
38
|
-
{location}
|
|
39
|
-
</span>
|
|
40
|
-
)
|
|
41
|
-
}
|
|
42
|
-
{type && <span class="text-muted-foreground text-xs">{type}</span>}
|
|
43
|
-
{
|
|
44
|
-
isTrue(required) && (
|
|
45
|
-
<span class="font-medium text-[0.625rem] text-red-600 uppercase tracking-wide dark:text-red-400">
|
|
46
|
-
required
|
|
47
|
-
</span>
|
|
48
|
-
)
|
|
49
|
-
}
|
|
50
|
-
{
|
|
51
|
-
isTrue(deprecated) && (
|
|
52
|
-
<span class="font-medium text-[0.625rem] text-muted-foreground uppercase tracking-wide line-through">
|
|
53
|
-
deprecated
|
|
54
|
-
</span>
|
|
55
|
-
)
|
|
56
|
-
}
|
|
57
|
-
</div>
|
|
58
|
-
{
|
|
59
|
-
hasDefault && (
|
|
60
|
-
<div class="mt-1 text-muted-foreground text-xs">
|
|
61
|
-
Default:{" "}
|
|
62
|
-
<code class="rounded bg-muted px-1 py-0.5 text-foreground">
|
|
63
|
-
{String(defaultValue)}
|
|
64
|
-
</code>
|
|
65
|
-
</div>
|
|
66
|
-
)
|
|
67
|
-
}
|
|
68
|
-
{
|
|
69
|
-
hasDescription && (
|
|
70
|
-
<div class="mt-1.5 text-muted-foreground text-sm [&>:first-child]:mt-0 [&>:last-child]:mb-0">
|
|
71
|
-
<slot />
|
|
72
|
-
</div>
|
|
73
|
-
)
|
|
74
|
-
}
|
|
75
|
-
</div>
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
// Mintlify-compat <ParamField>. Mintlify encodes the parameter's location in the
|
|
3
|
-
// attribute name (`<ParamField path="id" />`, `query`, `header`, `body`); the
|
|
4
|
-
// attribute's value is the field name. Falls back to a plain `name` prop.
|
|
5
|
-
import ApiField from "./ApiField.astro";
|
|
6
|
-
|
|
7
|
-
const {
|
|
8
|
-
body,
|
|
9
|
-
default: defaultValue,
|
|
10
|
-
deprecated,
|
|
11
|
-
header,
|
|
12
|
-
name,
|
|
13
|
-
path,
|
|
14
|
-
query,
|
|
15
|
-
required,
|
|
16
|
-
type,
|
|
17
|
-
} = Astro.props;
|
|
18
|
-
|
|
19
|
-
const locations = [
|
|
20
|
-
{ key: "path", value: path },
|
|
21
|
-
{ key: "query", value: query },
|
|
22
|
-
{ key: "header", value: header },
|
|
23
|
-
{ key: "body", value: body },
|
|
24
|
-
] as const;
|
|
25
|
-
const located = locations.find((entry) => typeof entry.value === "string");
|
|
26
|
-
const fieldName = located ? located.value : name;
|
|
27
|
-
const location = located?.key;
|
|
28
|
-
---
|
|
29
|
-
|
|
30
|
-
<ApiField
|
|
31
|
-
default={defaultValue}
|
|
32
|
-
deprecated={deprecated}
|
|
33
|
-
location={location}
|
|
34
|
-
name={fieldName}
|
|
35
|
-
required={required}
|
|
36
|
-
type={type}
|
|
37
|
-
>
|
|
38
|
-
<slot />
|
|
39
|
-
</ApiField>
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
// Mintlify-compat <RequestField>: same shape as <ResponseField> — a named field
|
|
3
|
-
// (`name`, `type`, `required`, `default`, `deprecated`) with a slotted body.
|
|
4
|
-
import ApiField from "./ApiField.astro";
|
|
5
|
-
|
|
6
|
-
const {
|
|
7
|
-
default: defaultValue,
|
|
8
|
-
deprecated,
|
|
9
|
-
name,
|
|
10
|
-
required,
|
|
11
|
-
type,
|
|
12
|
-
} = Astro.props;
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
<ApiField
|
|
16
|
-
default={defaultValue}
|
|
17
|
-
deprecated={deprecated}
|
|
18
|
-
name={name}
|
|
19
|
-
required={required}
|
|
20
|
-
type={type}
|
|
21
|
-
>
|
|
22
|
-
<slot />
|
|
23
|
-
</ApiField>
|