@kubb/renderer-jsx 5.0.0-beta.4 → 5.0.0-beta.41

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.
Files changed (45) hide show
  1. package/README.md +134 -0
  2. package/dist/index.cjs +621 -193
  3. package/dist/index.d.ts +326 -90
  4. package/dist/index.js +615 -193
  5. package/dist/jsx-dev-runtime.cjs +1 -1
  6. package/dist/jsx-dev-runtime.d.ts +7 -8
  7. package/dist/jsx-dev-runtime.js +2 -2
  8. package/dist/{jsx-namespace-CNp0arTN.d.ts → jsx-namespace-dmStM1a2.d.ts} +3 -3
  9. package/dist/{jsx-runtime-DdmO3p0U.cjs → jsx-runtime-4M1bV6ub.cjs} +9 -9
  10. package/dist/{jsx-runtime-Cvu_ZYgL.js → jsx-runtime-CQ6-_gue.js} +10 -10
  11. package/dist/jsx-runtime.cjs +1 -1
  12. package/dist/jsx-runtime.d.ts +9 -10
  13. package/dist/jsx-runtime.js +2 -2
  14. package/dist/{types-nAFMiWFw.d.ts → types-B5VGpHs0.d.ts} +11 -10
  15. package/dist/types.d.ts +1 -1
  16. package/package.json +8 -12
  17. package/src/Renderer.ts +1 -5
  18. package/src/Runtime.tsx +7 -18
  19. package/src/SyncRuntime.tsx +309 -0
  20. package/src/components/Callout.tsx +59 -0
  21. package/src/components/CodeBlock.tsx +37 -0
  22. package/src/components/Const.tsx +4 -4
  23. package/src/components/File.tsx +7 -5
  24. package/src/components/Frontmatter.tsx +38 -0
  25. package/src/components/Function.tsx +8 -8
  26. package/src/components/Heading.tsx +34 -0
  27. package/src/components/Jsx.tsx +1 -1
  28. package/src/components/List.tsx +40 -0
  29. package/src/components/Paragraph.tsx +28 -0
  30. package/src/components/Type.tsx +3 -3
  31. package/src/constants.ts +19 -9
  32. package/src/createRenderer.tsx +81 -62
  33. package/src/dom.ts +7 -19
  34. package/src/index.ts +7 -1
  35. package/src/types.ts +9 -8
  36. package/src/utils.ts +153 -174
  37. package/dist/index.cjs.map +0 -1
  38. package/dist/index.js.map +0 -1
  39. package/dist/jsx-dev-runtime.cjs.map +0 -1
  40. package/dist/jsx-dev-runtime.js.map +0 -1
  41. package/dist/jsx-runtime-Cvu_ZYgL.js.map +0 -1
  42. package/dist/jsx-runtime-DdmO3p0U.cjs.map +0 -1
  43. package/dist/jsx-runtime.cjs.map +0 -1
  44. package/dist/jsx-runtime.js.map +0 -1
  45. /package/dist/{chunk-Bb7HlUDG.js → chunk-DoukXa0m.js} +0 -0
@@ -0,0 +1,309 @@
1
+ import type { ArrowFunctionNode, CodeNode, ExportNode, FileNode, ImportNode, JSDocNode, SourceNode } from '@kubb/ast'
2
+ import {
3
+ createArrowFunction,
4
+ createBreak,
5
+ createConst,
6
+ createExport,
7
+ createFunction,
8
+ createImport,
9
+ createJsx,
10
+ createSource,
11
+ createText,
12
+ createType,
13
+ } from '@kubb/ast'
14
+ import React from 'react'
15
+ import { KUBB_ARROW_FUNCTION, KUBB_CONST, KUBB_EXPORT, KUBB_FILE, KUBB_FUNCTION, KUBB_IMPORT, KUBB_JSX, KUBB_SOURCE, KUBB_TYPE } from './constants.ts'
16
+ import type { KubbReactElement } from './types.ts'
17
+
18
+ type OnText = (text: string) => void
19
+ type OnHost = (type: string, props: Record<string, unknown>) => void
20
+
21
+ /**
22
+ * Walks `element`, resolving arrays, Fragments, and function components
23
+ * transparently, then calls `onText` for primitive values and `onHost` for
24
+ * every host element encountered. Pure function components are called
25
+ * synchronously. Hooks and class components are not supported.
26
+ */
27
+ function walkElement(element: unknown, onText: OnText, onHost: OnHost): void {
28
+ if (element == null || typeof element === 'boolean') return
29
+
30
+ if (typeof element === 'string' || typeof element === 'number' || typeof element === 'bigint') {
31
+ onText(String(element))
32
+ return
33
+ }
34
+
35
+ if (Array.isArray(element)) {
36
+ for (const child of element) walkElement(child, onText, onHost)
37
+ return
38
+ }
39
+
40
+ if (typeof element === 'object' && '$$typeof' in element) {
41
+ const el = element as unknown as React.ReactElement
42
+ const { type } = el
43
+ const props = el.props as Record<string, unknown>
44
+
45
+ if (type === React.Fragment) {
46
+ walkElement(props['children'], onText, onHost)
47
+ return
48
+ }
49
+ if (typeof type === 'function') {
50
+ walkElement((type as (p: unknown) => unknown)(props), onText, onHost)
51
+ return
52
+ }
53
+ if (typeof type === 'string') {
54
+ onHost(type, props)
55
+ }
56
+ }
57
+ }
58
+
59
+ function toBool(val: unknown): boolean {
60
+ return (val ?? false) as boolean
61
+ }
62
+
63
+ function collectCodeNodes(props: Record<string, unknown>): Array<CodeNode> {
64
+ const nodes: Array<CodeNode> = []
65
+ collectCode(props['children'], nodes)
66
+ return nodes
67
+ }
68
+
69
+ function collectCode(element: unknown, nodes: Array<CodeNode>): void {
70
+ walkElement(
71
+ element,
72
+ (text) => {
73
+ if (text.trim()) nodes.push(createText(text))
74
+ },
75
+ (type, props) => resolveCodeNode(type, props, nodes),
76
+ )
77
+ }
78
+
79
+ function resolveCodeNode(type: string, props: Record<string, unknown>, nodes: Array<CodeNode>): void {
80
+ if (type === 'br') {
81
+ nodes.push(createBreak())
82
+ return
83
+ }
84
+
85
+ if (type === KUBB_JSX) {
86
+ let value = ''
87
+ walkElement(
88
+ props['children'],
89
+ (t) => {
90
+ value += t
91
+ },
92
+ () => {},
93
+ )
94
+ if (value) nodes.push(createJsx(value))
95
+ return
96
+ }
97
+
98
+ if (type === KUBB_FUNCTION) {
99
+ nodes.push(
100
+ createFunction({
101
+ name: props['name'] as string,
102
+ params: props['params'] as string | null | undefined,
103
+ export: props['export'] as boolean | null | undefined,
104
+ default: props['default'] as boolean | null | undefined,
105
+ async: props['async'] as boolean | null | undefined,
106
+ generics: props['generics'] as string | Array<string> | null | undefined,
107
+ returnType: props['returnType'] as string | null | undefined,
108
+ JSDoc: props['JSDoc'] as JSDocNode | null | undefined,
109
+ nodes: collectCodeNodes(props),
110
+ }),
111
+ )
112
+ return
113
+ }
114
+
115
+ if (type === KUBB_ARROW_FUNCTION) {
116
+ nodes.push(
117
+ createArrowFunction({
118
+ name: props['name'] as string,
119
+ params: props['params'] as string | null | undefined,
120
+ export: props['export'] as boolean | null | undefined,
121
+ default: props['default'] as boolean | null | undefined,
122
+ async: props['async'] as boolean | null | undefined,
123
+ generics: props['generics'] as string | Array<string> | null | undefined,
124
+ returnType: props['returnType'] as string | null | undefined,
125
+ singleLine: props['singleLine'] as boolean | null | undefined,
126
+ JSDoc: props['JSDoc'] as JSDocNode | null | undefined,
127
+ nodes: collectCodeNodes(props),
128
+ } as Omit<ArrowFunctionNode, 'kind'>),
129
+ )
130
+ return
131
+ }
132
+
133
+ if (type === KUBB_CONST) {
134
+ nodes.push(
135
+ createConst({
136
+ name: props['name'] as string,
137
+ type: props['type'] as string | null | undefined,
138
+ export: props['export'] as boolean | null | undefined,
139
+ asConst: props['asConst'] as boolean | null | undefined,
140
+ JSDoc: props['JSDoc'] as JSDocNode | null | undefined,
141
+ nodes: collectCodeNodes(props),
142
+ }),
143
+ )
144
+ return
145
+ }
146
+
147
+ if (type === KUBB_TYPE) {
148
+ nodes.push(
149
+ createType({
150
+ name: props['name'] as string,
151
+ export: props['export'] as boolean | null | undefined,
152
+ JSDoc: props['JSDoc'] as JSDocNode | null | undefined,
153
+ nodes: collectCodeNodes(props),
154
+ }),
155
+ )
156
+ return
157
+ }
158
+ }
159
+
160
+ type FileChildren = { sources: Array<SourceNode>; exports: Array<ExportNode>; imports: Array<ImportNode> }
161
+
162
+ function collectFileChildren(element: unknown): FileChildren {
163
+ const sources: Array<SourceNode> = []
164
+ const exports: Array<ExportNode> = []
165
+ const imports: Array<ImportNode> = []
166
+
167
+ walkElement(
168
+ element,
169
+ (text) => {
170
+ if (text.trim()) {
171
+ throw new Error(`[react] '${text}' should be part of <File.Source> component when using the <File/> component`)
172
+ }
173
+ },
174
+ (type, props) => {
175
+ if (type === KUBB_SOURCE) {
176
+ sources.push(
177
+ createSource({
178
+ name: props['name']?.toString(),
179
+ isTypeOnly: toBool(props['isTypeOnly']),
180
+ isExportable: toBool(props['isExportable']),
181
+ isIndexable: toBool(props['isIndexable']),
182
+ nodes: collectCodeNodes(props),
183
+ }),
184
+ )
185
+ return
186
+ }
187
+
188
+ if (type === KUBB_EXPORT) {
189
+ exports.push(
190
+ createExport({
191
+ name: props['name'] as ExportNode['name'],
192
+ path: props['path'] as string,
193
+ isTypeOnly: toBool(props['isTypeOnly']),
194
+ asAlias: toBool(props['asAlias']),
195
+ }),
196
+ )
197
+ return
198
+ }
199
+
200
+ if (type === KUBB_IMPORT) {
201
+ imports.push(
202
+ createImport({
203
+ name: props['name'] as ImportNode['name'],
204
+ path: props['path'] as string,
205
+ root: props['root'] as string | null | undefined,
206
+ isTypeOnly: toBool(props['isTypeOnly']),
207
+ isNameSpace: toBool(props['isNameSpace']),
208
+ }),
209
+ )
210
+ return
211
+ }
212
+
213
+ const nested = collectFileChildren(props['children'])
214
+ sources.push(...nested.sources)
215
+ exports.push(...nested.exports)
216
+ imports.push(...nested.imports)
217
+ },
218
+ )
219
+
220
+ return { sources, exports, imports }
221
+ }
222
+
223
+ function* walkFiles(element: unknown): Generator<FileNode> {
224
+ if (element == null || typeof element === 'boolean') return
225
+
226
+ if (typeof element === 'string' || typeof element === 'number' || typeof element === 'bigint') return
227
+
228
+ if (Array.isArray(element)) {
229
+ for (const child of element) yield* walkFiles(child)
230
+ return
231
+ }
232
+
233
+ if (typeof element === 'object' && '$$typeof' in element) {
234
+ const el = element as unknown as React.ReactElement
235
+ const { type } = el
236
+ const props = el.props as Record<string, unknown>
237
+
238
+ if (type === React.Fragment) {
239
+ yield* walkFiles(props['children'])
240
+ return
241
+ }
242
+
243
+ if (typeof type === 'function') {
244
+ yield* walkFiles((type as (p: unknown) => unknown)(props))
245
+ return
246
+ }
247
+
248
+ if (typeof type === 'string') {
249
+ if (type === KUBB_FILE && props['baseName'] !== undefined && props['path'] !== undefined) {
250
+ const { sources, exports, imports } = collectFileChildren(props['children'])
251
+ yield {
252
+ baseName: props['baseName'],
253
+ path: props['path'],
254
+ meta: props['meta'] || {},
255
+ footer: props['footer'],
256
+ banner: props['banner'],
257
+ sources,
258
+ exports,
259
+ imports,
260
+ } as FileNode
261
+ } else {
262
+ yield* walkFiles(props['children'])
263
+ }
264
+ }
265
+ }
266
+ }
267
+
268
+ /**
269
+ * Synchronous JSX renderer that walks the element tree in a single pass,
270
+ * producing {@link FileNode} objects directly without an intermediate virtual
271
+ * DOM. No React fiber, scheduler, or work loop is involved.
272
+ *
273
+ * All components must be pure functions. Hooks and class components are not
274
+ * supported. Produces identical output to the React-backed {@link Runtime} at
275
+ * approximately 2, 4× the speed and a fraction of the allocations.
276
+ */
277
+ export class SyncRuntime {
278
+ /**
279
+ * Accumulated {@link FileNode} results from every {@link render} call.
280
+ */
281
+ nodes: Array<FileNode> = []
282
+
283
+ /**
284
+ * Walks `element` synchronously, converts every `<kubb-file>` subtree into
285
+ * a {@link FileNode} with no intermediate virtual DOM, and appends the results
286
+ * to {@link nodes}.
287
+ */
288
+ render(element: KubbReactElement): void {
289
+ for (const file of walkFiles(element)) {
290
+ this.nodes.push(file)
291
+ }
292
+ }
293
+
294
+ /**
295
+ * Walks `element` synchronously and yields each {@link FileNode} as it is
296
+ * produced, without buffering into an intermediate array first. Callers can
297
+ * begin processing each file before the rest of the element tree is traversed.
298
+ *
299
+ * @example
300
+ * ```ts
301
+ * for (const file of runtime.stream(element)) {
302
+ * await writeFile(file)
303
+ * }
304
+ * ```
305
+ */
306
+ *stream(element: KubbReactElement): Generator<FileNode> {
307
+ yield* walkFiles(element)
308
+ }
309
+ }
@@ -0,0 +1,59 @@
1
+ import type { Key, KubbReactElement } from '../types.ts'
2
+
3
+ const CALLOUT_LABEL = {
4
+ tip: 'TIP',
5
+ note: 'NOTE',
6
+ important: 'IMPORTANT',
7
+ warning: 'WARNING',
8
+ caution: 'CAUTION',
9
+ } as const
10
+
11
+ export type CalloutType = keyof typeof CALLOUT_LABEL
12
+
13
+ type Props = {
14
+ key?: Key
15
+ /**
16
+ * Callout kind. Maps to the uppercase label inside the `> [!TYPE]` marker.
17
+ */
18
+ type: CalloutType
19
+ /**
20
+ * Optional title rendered on the same line as the marker.
21
+ */
22
+ title?: string | null
23
+ /**
24
+ * Body text. Each line is quoted with `> ` so multi-line content stays
25
+ * inside the callout block.
26
+ */
27
+ children: string
28
+ }
29
+
30
+ /**
31
+ * Renders a GitHub-style alert callout, portable across GitHub, GitLab,
32
+ * VitePress, Obsidian, and MDX.
33
+ *
34
+ * Emits a `<File.Source>` block containing `> [!TYPE] Title` followed by the
35
+ * body with every line prefixed by `> `.
36
+ *
37
+ * @example
38
+ * ```tsx
39
+ * <Callout type="tip">Run `kubb start --watch` to keep the generator hot.</Callout>
40
+ * // > [!TIP]
41
+ * // > Run `kubb start --watch` to keep the generator hot.
42
+ *
43
+ * <Callout type="warning" title="Heads up">Breaking change in v6.</Callout>
44
+ * // > [!WARNING] Heads up
45
+ * // > Breaking change in v6.
46
+ * ```
47
+ */
48
+ export function Callout({ type, title, children }: Props): KubbReactElement {
49
+ const label = CALLOUT_LABEL[type]
50
+ const header = title ? `> [!${label}] ${title}` : `> [!${label}]`
51
+ const quoted = children
52
+ .trimEnd()
53
+ .split('\n')
54
+ .map((line) => (line.length > 0 ? `> ${line}` : '>'))
55
+ .join('\n')
56
+ return <kubb-source name="callout">{`${header}\n${quoted}`}</kubb-source>
57
+ }
58
+
59
+ Callout.displayName = 'Callout'
@@ -0,0 +1,37 @@
1
+ import type { Key, KubbReactElement } from '../types.ts'
2
+
3
+ type Props = {
4
+ key?: Key
5
+ /**
6
+ * Language tag for syntax highlighting. Rendered after the opening fence.
7
+ *
8
+ * @example
9
+ * `lang: 'typescript'`
10
+ */
11
+ lang?: string
12
+ /**
13
+ * Code body. Wrapped in triple-backtick fences as-is.
14
+ */
15
+ children: string
16
+ }
17
+
18
+ /**
19
+ * Renders a fenced markdown code block.
20
+ *
21
+ * Emits a `<File.Source>` block containing the children wrapped in
22
+ * triple-backtick fences with an optional language tag.
23
+ *
24
+ * @example
25
+ * ```tsx
26
+ * <CodeBlock lang="typescript">{'const pet = { id: 1 }'}</CodeBlock>
27
+ * // ```typescript
28
+ * // const pet = { id: 1 }
29
+ * // ```
30
+ * ```
31
+ */
32
+ export function CodeBlock({ lang, children }: Props): KubbReactElement {
33
+ const fence = `\`\`\`${lang ?? ''}\n${children}\n\`\`\``
34
+ return <kubb-source name="codeBlock">{fence}</kubb-source>
35
+ }
36
+
37
+ CodeBlock.displayName = 'CodeBlock'
@@ -15,26 +15,26 @@ type ConstProps = {
15
15
  * - `false` generates `const name = …`
16
16
  * @default false
17
17
  */
18
- export?: boolean
18
+ export?: boolean | null
19
19
  /**
20
20
  * TypeScript type annotation for the constant, written verbatim after `const name:`.
21
21
  *
22
22
  * @example
23
23
  * `type: 'Pet'` → `const pet: Pet = …`
24
24
  */
25
- type?: string
25
+ type?: string | null
26
26
  /**
27
27
  * JSDoc block to prepend to the constant declaration.
28
28
  * Each entry in `comments` becomes one line inside the emitted `/** … *\/` block.
29
29
  */
30
- JSDoc?: JSDoc
30
+ JSDoc?: JSDoc | null
31
31
  /**
32
32
  * Append `as const` after the initialiser, enabling TypeScript const assertions.
33
33
  * - `true` generates `const name = … as const`
34
34
  * - `false` generates `const name = …`
35
35
  * @default false
36
36
  */
37
- asConst?: boolean
37
+ asConst?: boolean | null
38
38
  /**
39
39
  * Child nodes rendered as the initialiser expression of the constant.
40
40
  */
@@ -25,9 +25,9 @@ type BasePropsWithoutBaseName = {
25
25
  baseName?: never
26
26
  /**
27
27
  * Fully qualified path to the generated file.
28
- * Optional when `baseName` is omitted the component renders its children inline.
28
+ * Optional when `baseName` is omitted, the component renders its children inline.
29
29
  */
30
- path?: string
30
+ path?: string | null
31
31
  }
32
32
 
33
33
  type BaseProps = BasePropsWithBaseName | BasePropsWithoutBaseName
@@ -38,15 +38,17 @@ type Props<TMeta> = BaseProps & {
38
38
  * Arbitrary metadata attached to the file node.
39
39
  * Used by plugins for barrel generation and custom post-processing.
40
40
  */
41
- meta?: TMeta
41
+ meta?: TMeta | null
42
42
  /**
43
43
  * Text prepended to the generated file content before any source blocks.
44
+ * Accepts `null` so `resolver.resolveBanner()` results can be passed directly.
44
45
  */
45
- banner?: string
46
+ banner?: string | null
46
47
  /**
47
48
  * Text appended to the generated file content after all source blocks.
49
+ * Accepts `null` so `resolver.resolveFooter()` results can be passed directly.
48
50
  */
49
- footer?: string
51
+ footer?: string | null
50
52
  /**
51
53
  * Child nodes rendered as the content of this file (source blocks, imports, exports).
52
54
  */
@@ -0,0 +1,38 @@
1
+ import { stringify } from 'yaml'
2
+ import type { Key, KubbReactElement } from '../types.ts'
3
+
4
+ type Props = {
5
+ key?: Key
6
+ /**
7
+ * Plain object serialised as YAML between `---` fences.
8
+ *
9
+ * @example
10
+ * `data: { title: 'Pets', layout: 'doc' }`
11
+ */
12
+ data: Record<string, unknown>
13
+ }
14
+
15
+ /**
16
+ * Emits a YAML frontmatter envelope at the top of a generated markdown file.
17
+ *
18
+ * Renders a `<File.Source>` block containing `---\n<yaml>\n---`. Place it as
19
+ * the first child of `<File>` so it appears at the top of the output. Pair with
20
+ * `parserMd` to write `.md` files that downstream tooling (VitePress, MDX,
21
+ * static-site generators) treats as frontmatter.
22
+ *
23
+ * @example Page frontmatter at the top of a generated markdown file
24
+ * ```tsx
25
+ * <File baseName="pets.md" path="src/pets.md">
26
+ * <Frontmatter data={{ title: 'Pets', layout: 'doc' }} />
27
+ * <File.Source>
28
+ * {'# Pets\n\nList of pets.'}
29
+ * </File.Source>
30
+ * </File>
31
+ * ```
32
+ */
33
+ export function Frontmatter({ data }: Props): KubbReactElement {
34
+ const envelope = `---\n${stringify(data).trimEnd()}\n---`
35
+ return <kubb-source name="frontmatter">{envelope}</kubb-source>
36
+ }
37
+
38
+ Frontmatter.displayName = 'Frontmatter'
@@ -14,28 +14,28 @@ type Props = {
14
14
  * Requires `export` to also be `true`.
15
15
  * @default false
16
16
  */
17
- default?: boolean
17
+ default?: boolean | null
18
18
  /**
19
19
  * Parameter list written verbatim between the function's parentheses.
20
20
  *
21
21
  * @example
22
22
  * `params: 'petId: string, options?: RequestOptions'`
23
23
  */
24
- params?: string
24
+ params?: string | null
25
25
  /**
26
26
  * Emit the `export` keyword before the function declaration.
27
27
  * - `true` generates `export function name(…) { … }`
28
28
  * - `false` generates `function name(…) { … }`
29
29
  * @default false
30
30
  */
31
- export?: boolean
31
+ export?: boolean | null
32
32
  /**
33
33
  * Emit the `async` keyword, making this an async function.
34
34
  * The return type is automatically wrapped in `Promise<returnType>` when both
35
35
  * `async` and `returnType` are set.
36
36
  * @default false
37
37
  */
38
- async?: boolean
38
+ async?: boolean | null
39
39
  /**
40
40
  * TypeScript generic type parameters written verbatim between `<` and `>`.
41
41
  * Pass an array to emit multiple parameters separated by commas.
@@ -46,7 +46,7 @@ type Props = {
46
46
  * @example Multiple generics
47
47
  * `generics: ['TData', 'TError = unknown']`
48
48
  */
49
- generics?: string | string[]
49
+ generics?: string | Array<string> | null
50
50
  /**
51
51
  * TypeScript return type annotation written verbatim after `:`.
52
52
  * When `async` is `true`, the value is automatically wrapped in `Promise<…>`.
@@ -54,12 +54,12 @@ type Props = {
54
54
  * @example
55
55
  * `returnType: 'Pet'`
56
56
  */
57
- returnType?: string
57
+ returnType?: string | null
58
58
  /**
59
59
  * JSDoc block to prepend to the function declaration.
60
60
  * Each entry in `comments` becomes one line inside the emitted `/** … *\/` block.
61
61
  */
62
- JSDoc?: JSDoc
62
+ JSDoc?: JSDoc | null
63
63
  /**
64
64
  * Child nodes rendered as the body of the function.
65
65
  */
@@ -110,7 +110,7 @@ type ArrowFunctionProps = Props & {
110
110
  * - `false` generates `const name = (…) => { … }`
111
111
  * @default false
112
112
  */
113
- singleLine?: boolean
113
+ singleLine?: boolean | null
114
114
  }
115
115
 
116
116
  /**
@@ -0,0 +1,34 @@
1
+ import type { Key, KubbReactElement } from '../types.ts'
2
+
3
+ type Level = 1 | 2 | 3 | 4 | 5 | 6
4
+
5
+ type Props = {
6
+ key?: Key
7
+ /**
8
+ * Heading depth, `1` through `6`. Matches the number of `#` characters
9
+ * prefixed to the heading text.
10
+ */
11
+ level: Level
12
+ /**
13
+ * Heading text. Inline markdown (links, emphasis) is passed through verbatim.
14
+ */
15
+ children: string
16
+ }
17
+
18
+ /**
19
+ * Renders an ATX-style markdown heading.
20
+ *
21
+ * Emits a `<File.Source>` block containing `${'#'.repeat(level)} ${children}`.
22
+ * Use inside a `<File>` rendered by `parserMd`.
23
+ *
24
+ * @example
25
+ * ```tsx
26
+ * <Heading level={2}>Installation</Heading>
27
+ * // ## Installation
28
+ * ```
29
+ */
30
+ export function Heading({ level, children }: Props): KubbReactElement {
31
+ return <kubb-source name="heading">{`${'#'.repeat(level)} ${children}`}</kubb-source>
32
+ }
33
+
34
+ Heading.displayName = 'Heading'
@@ -17,7 +17,7 @@ type Props = {
17
17
  *
18
18
  * Use this component when you need to include JSX markup (including fragments
19
19
  * `<>…</>`) in the body of a generated function or component. The `children`
20
- * prop must be a plain string expression attributes that reference runtime
20
+ * prop must be a plain string, expression attributes that reference runtime
21
21
  * values should be written as template literals.
22
22
  *
23
23
  * @example
@@ -0,0 +1,40 @@
1
+ import type { Key, KubbReactElement } from '../types.ts'
2
+
3
+ type Props = {
4
+ key?: Key
5
+ /**
6
+ * When `true`, emits a numbered list (`1. …`). When `false` or omitted,
7
+ * emits a bullet list (`- …`).
8
+ *
9
+ * @default false
10
+ */
11
+ ordered?: boolean | null
12
+ /**
13
+ * One entry per line. Inline markdown is passed through verbatim.
14
+ */
15
+ items: ReadonlyArray<string>
16
+ }
17
+
18
+ /**
19
+ * Renders a markdown list.
20
+ *
21
+ * Emits a `<File.Source>` block containing one entry per line, prefixed with
22
+ * `1.` / `2.` … when `ordered`, or `-` otherwise.
23
+ *
24
+ * @example
25
+ * ```tsx
26
+ * <List items={['Add the parser', 'Render the page']} />
27
+ * // - Add the parser
28
+ * // - Render the page
29
+ *
30
+ * <List ordered items={['First', 'Second']} />
31
+ * // 1. First
32
+ * // 2. Second
33
+ * ```
34
+ */
35
+ export function List({ ordered, items }: Props): KubbReactElement {
36
+ const body = items.map((item, index) => `${ordered ? `${index + 1}.` : '-'} ${item}`).join('\n')
37
+ return <kubb-source name="list">{body}</kubb-source>
38
+ }
39
+
40
+ List.displayName = 'List'
@@ -0,0 +1,28 @@
1
+ import type { Key, KubbReactElement } from '../types.ts'
2
+
3
+ type Props = {
4
+ key?: Key
5
+ /**
6
+ * Paragraph text. Inline markdown (links, emphasis, code spans) is passed
7
+ * through verbatim.
8
+ */
9
+ children: string
10
+ }
11
+
12
+ /**
13
+ * Renders a markdown paragraph.
14
+ *
15
+ * Emits a `<File.Source>` block containing the text as-is. Paragraphs are
16
+ * separated from surrounding blocks by blank lines via the parser's source
17
+ * joining.
18
+ *
19
+ * @example
20
+ * ```tsx
21
+ * <Paragraph>{'A pet object with `id` and `name` fields.'}</Paragraph>
22
+ * ```
23
+ */
24
+ export function Paragraph({ children }: Props): KubbReactElement {
25
+ return <kubb-source name="paragraph">{children}</kubb-source>
26
+ }
27
+
28
+ Paragraph.displayName = 'Paragraph'