@kubb/renderer-jsx 5.0.0-beta.3 → 5.0.0-beta.31

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.
@@ -1,6 +1,6 @@
1
1
  import { n as __name } from "./chunk-Bb7HlUDG.js";
2
- import { h as KubbReactNode, m as KubbReactElement } from "./types-nAFMiWFw.js";
3
- import { t as JSX } from "./jsx-namespace-CNp0arTN.js";
2
+ import { h as KubbReactNode, m as KubbReactElement } from "./types-Cur_nGCL.js";
3
+ import { t as JSX } from "./jsx-namespace-D_TStJE_.js";
4
4
  import * as _$react from "react";
5
5
  import * as React$1 from "react/jsx-runtime";
6
6
 
@@ -1,5 +1,5 @@
1
1
  import { n as __name } from "./chunk-Bb7HlUDG.js";
2
- import { _ as KubbTextProps, c as KubbConstProps, d as KubbFunctionProps, f as KubbImportProps, g as KubbSourceProps, h as KubbReactNode, l as KubbExportProps, m as KubbReactElement, p as KubbJsxProps, s as KubbArrowFunctionProps, u as KubbFileProps, v as KubbTypeProps, y as LineBreakProps } from "./types-nAFMiWFw.js";
2
+ import { _ as KubbTextProps, c as KubbConstProps, d as KubbFunctionProps, f as KubbImportProps, g as KubbSourceProps, h as KubbReactNode, l as KubbExportProps, m as KubbReactElement, p as KubbJsxProps, s as KubbArrowFunctionProps, u as KubbFileProps, v as KubbTypeProps, y as LineBreakProps } from "./types-Cur_nGCL.js";
3
3
  import React from "react";
4
4
 
5
5
  //#region src/jsx-namespace.d.ts
@@ -36,4 +36,4 @@ declare namespace JSX$1 {
36
36
  }
37
37
  //#endregion
38
38
  export { JSX$1 as t };
39
- //# sourceMappingURL=jsx-namespace-CNp0arTN.d.ts.map
39
+ //# sourceMappingURL=jsx-namespace-D_TStJE_.d.ts.map
@@ -1,6 +1,6 @@
1
1
  import { n as __name } from "./chunk-Bb7HlUDG.js";
2
- import { h as KubbReactNode, m as KubbReactElement } from "./types-nAFMiWFw.js";
3
- import { t as JSX } from "./jsx-namespace-CNp0arTN.js";
2
+ import { h as KubbReactNode, m as KubbReactElement } from "./types-Cur_nGCL.js";
3
+ import { t as JSX } from "./jsx-namespace-D_TStJE_.js";
4
4
  import * as _$react from "react";
5
5
  import * as React$1 from "react/jsx-runtime";
6
6
 
@@ -13,13 +13,14 @@ type Key = string | number | bigint;
13
13
  */
14
14
  type ElementNames = 'br' | 'div' | 'indent' | 'dedent' | 'kubb-jsx' | 'kubb-text' | 'kubb-file' | 'kubb-source' | 'kubb-import' | 'kubb-export' | 'kubb-function' | 'kubb-arrow-function' | 'kubb-const' | 'kubb-type' | 'kubb-root' | 'kubb-app';
15
15
  type Node = {
16
- parentNode: DOMElement | undefined;
16
+ parentNode: DOMElement | null;
17
17
  internal_static?: boolean;
18
18
  };
19
19
  /**
20
20
  * Allowed attribute value types for DOM elements.
21
+ * `null` signals intentionally empty — the prop was explicitly cleared.
21
22
  */
22
- type DOMNodeAttribute = boolean | string | number | Record<string, unknown> | Array<unknown>;
23
+ type DOMNodeAttribute = boolean | string | number | null | Record<string, unknown> | Array<unknown>;
23
24
  type TextName = '#text';
24
25
  /**
25
26
  * Leaf DOM node containing raw text.
@@ -46,11 +47,11 @@ type DOMElement = {
46
47
  /**
47
48
  * Key/value attributes passed as JSX props to this element.
48
49
  */
49
- attributes: Map<string, DOMNodeAttribute>;
50
+ attributes: Record<string, DOMNodeAttribute>;
50
51
  /**
51
52
  * Ordered list of child nodes attached to this element.
52
53
  */
53
- childNodes: DOMNode[];
54
+ childNodes: Array<DOMNode>;
54
55
  internal_transform?: OutputTransformer;
55
56
  isStaticDirty?: boolean;
56
57
  staticNode?: DOMElement;
@@ -86,12 +87,12 @@ type KubbTextProps = {
86
87
  * Represents a generated file.
87
88
  */
88
89
  type KubbFileProps = {
89
- id?: string;
90
+ id?: string | null;
90
91
  children?: KubbReactNode;
91
92
  baseName: string;
92
93
  path: string;
93
- override?: boolean;
94
- meta?: FileNode['meta'];
94
+ override?: boolean | null;
95
+ meta?: FileNode['meta'] | null;
95
96
  };
96
97
  /**
97
98
  * Props for the `<kubb-source>` element.
@@ -165,4 +166,4 @@ type JSDoc = {
165
166
  };
166
167
  //#endregion
167
168
  export { KubbTextProps as _, JSDoc as a, TextNode as b, KubbConstProps as c, KubbFunctionProps as d, KubbImportProps as f, KubbSourceProps as g, KubbReactNode as h, ElementNames as i, KubbExportProps as l, KubbReactElement as m, DOMNode as n, Key as o, KubbJsxProps as p, DOMNodeAttribute as r, KubbArrowFunctionProps as s, DOMElement as t, KubbFileProps as u, KubbTypeProps as v, LineBreakProps as y };
168
- //# sourceMappingURL=types-nAFMiWFw.d.ts.map
169
+ //# sourceMappingURL=types-Cur_nGCL.d.ts.map
package/dist/types.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { _ as KubbTextProps, a as JSDoc, b as TextNode, c as KubbConstProps, d as KubbFunctionProps, f as KubbImportProps, g as KubbSourceProps, h as KubbReactNode, i as ElementNames, l as KubbExportProps, m as KubbReactElement, n as DOMNode, o as Key, p as KubbJsxProps, r as DOMNodeAttribute, s as KubbArrowFunctionProps, t as DOMElement, u as KubbFileProps, v as KubbTypeProps, y as LineBreakProps } from "./types-nAFMiWFw.js";
1
+ import { _ as KubbTextProps, a as JSDoc, b as TextNode, c as KubbConstProps, d as KubbFunctionProps, f as KubbImportProps, g as KubbSourceProps, h as KubbReactNode, i as ElementNames, l as KubbExportProps, m as KubbReactElement, n as DOMNode, o as Key, p as KubbJsxProps, r as DOMNodeAttribute, s as KubbArrowFunctionProps, t as DOMElement, u as KubbFileProps, v as KubbTypeProps, y as LineBreakProps } from "./types-Cur_nGCL.js";
2
2
  export { DOMElement, DOMNode, DOMNodeAttribute, ElementNames, JSDoc, Key, KubbArrowFunctionProps, KubbConstProps, KubbExportProps, KubbFileProps, KubbFunctionProps, KubbImportProps, KubbJsxProps, KubbReactElement, KubbReactNode, KubbSourceProps, KubbTextProps, KubbTypeProps, LineBreakProps, TextNode };
package/package.json CHANGED
@@ -1,19 +1,13 @@
1
1
  {
2
2
  "name": "@kubb/renderer-jsx",
3
- "version": "5.0.0-beta.3",
4
- "description": "React integration for Kubb - JSX runtime and component-based code generation with React reconciler for building type-safe generators",
3
+ "version": "5.0.0-beta.31",
4
+ "description": "JSX-based renderer for Kubb. Provides a custom React runtime, reconciler, and built-in components (File, Function, Type, Const) for component-based, type-safe code generation.",
5
5
  "keywords": [
6
- "code-generator",
7
6
  "codegen",
8
- "component-generation",
9
- "file-generation",
10
7
  "jsx",
11
8
  "jsx-runtime",
12
9
  "kubb",
13
- "plugin-system",
14
- "plugins",
15
10
  "react",
16
- "react-reconciler",
17
11
  "typescript"
18
12
  ],
19
13
  "license": "MIT",
@@ -81,10 +75,11 @@
81
75
  "registry": "https://registry.npmjs.org/"
82
76
  },
83
77
  "dependencies": {
84
- "@kubb/ast": "5.0.0-beta.3"
78
+ "yaml": "^2.9.0",
79
+ "@kubb/ast": "5.0.0-beta.31"
85
80
  },
86
81
  "devDependencies": {
87
- "@types/react": "^19.2.14",
82
+ "@types/react": "^19.2.15",
88
83
  "@types/react-reconciler": "0.33.0",
89
84
  "react": "19.2.5",
90
85
  "react-reconciler": "0.33.0",
package/src/Renderer.ts CHANGED
@@ -105,7 +105,7 @@ export const Renderer = Reconciler({
105
105
  return false
106
106
  },
107
107
  supportsMutation: true,
108
- isPrimaryRenderer: true,
108
+ isPrimaryRenderer: false,
109
109
  supportsPersistence: false,
110
110
  supportsHydration: false,
111
111
  scheduleTimeout: setTimeout,
@@ -155,9 +155,7 @@ export const Renderer = Reconciler({
155
155
  maySuspendCommit() {
156
156
  return false
157
157
  },
158
- // eslint-disable-next-line @typescript-eslint/naming-convention
159
158
  NotPendingTransition: undefined,
160
- // eslint-disable-next-line @typescript-eslint/naming-convention
161
159
  HostTransitionContext: createContext(null) as unknown as ReactContext<unknown>,
162
160
  resetFormInstance() {},
163
161
  requestPostPaintCallback() {},
@@ -180,5 +178,3 @@ export const Renderer = Reconciler({
180
178
  return null
181
179
  },
182
180
  })
183
-
184
- export type { FiberRoot } from 'react-reconciler'
package/src/Runtime.tsx CHANGED
@@ -1,12 +1,12 @@
1
1
  import { onProcessExit } from '@internals/utils'
2
2
  import type { FileNode } from '@kubb/ast'
3
- import { ConcurrentRoot } from 'react-reconciler/constants.js'
3
+ import { LegacyRoot } from 'react-reconciler/constants.js'
4
4
  import { Root } from './components/Root.tsx'
5
5
  import { createNode } from './dom.ts'
6
- import type { FiberRoot } from './Renderer.ts'
6
+ import type { FiberRoot } from 'react-reconciler'
7
7
  import { Renderer } from './Renderer.ts'
8
8
  import type { DOMElement, KubbReactElement } from './types.ts'
9
- import { processFiles } from './utils.ts'
9
+ import { collectFiles } from './utils.ts'
10
10
 
11
11
  type Options = {
12
12
  /**
@@ -22,11 +22,11 @@ export class Runtime {
22
22
 
23
23
  exitPromise?: Promise<void>
24
24
 
25
- nodes: FileNode[] = []
25
+ nodes: Array<FileNode> = []
26
26
  readonly #container: FiberRoot
27
27
  readonly #rootNode: DOMElement
28
28
 
29
- constructor(options: Options) {
29
+ constructor(options: Options = {}) {
30
30
  this.#options = options
31
31
  this.#rootNode = createNode('kubb-root')
32
32
  this.#rootNode.onRender = this.onRender
@@ -51,7 +51,7 @@ export class Runtime {
51
51
 
52
52
  const logRecoverableError = typeof reportError === 'function' ? reportError : console.error
53
53
 
54
- const rootTag = ConcurrentRoot
54
+ const rootTag = LegacyRoot
55
55
  this.#container = Renderer.createContainer(
56
56
  this.#rootNode,
57
57
  rootTag,
@@ -86,7 +86,7 @@ export class Runtime {
86
86
  return
87
87
  }
88
88
 
89
- const files = processFiles(this.#rootNode)
89
+ const files = collectFiles(this.#rootNode)
90
90
 
91
91
  this.nodes.push(...files)
92
92
 
@@ -156,15 +156,4 @@ export class Runtime {
156
156
 
157
157
  this.resolveExitPromise()
158
158
  }
159
-
160
- async waitUntilExit(): Promise<void> {
161
- if (!this.exitPromise) {
162
- this.exitPromise = new Promise((resolve, reject) => {
163
- this.resolveExitPromise = resolve
164
- this.rejectExitPromise = reject
165
- })
166
- }
167
-
168
- return this.exitPromise
169
- }
170
159
  }
@@ -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
  */
@@ -27,7 +27,7 @@ type BasePropsWithoutBaseName = {
27
27
  * Fully qualified path to the generated file.
28
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
  */