@kubb/renderer-jsx 5.0.0-beta.2 → 5.0.0-beta.21

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-D3-ni438.js";
3
+ import { t as JSX } from "./jsx-namespace-C7dcxEyT.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-D3-ni438.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-C7dcxEyT.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-D3-ni438.js";
3
+ import { t as JSX } from "./jsx-namespace-C7dcxEyT.js";
4
4
  import * as _$react from "react";
5
5
  import * as React$1 from "react/jsx-runtime";
6
6
 
@@ -13,7 +13,7 @@ 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
  /**
@@ -46,7 +46,7 @@ type DOMElement = {
46
46
  /**
47
47
  * Key/value attributes passed as JSX props to this element.
48
48
  */
49
- attributes: Map<string, DOMNodeAttribute>;
49
+ attributes: Record<string, DOMNodeAttribute>;
50
50
  /**
51
51
  * Ordered list of child nodes attached to this element.
52
52
  */
@@ -165,4 +165,4 @@ type JSDoc = {
165
165
  };
166
166
  //#endregion
167
167
  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
168
+ //# sourceMappingURL=types-D3-ni438.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-D3-ni438.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.2",
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.21",
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,7 +75,7 @@
81
75
  "registry": "https://registry.npmjs.org/"
82
76
  },
83
77
  "dependencies": {
84
- "@kubb/ast": "5.0.0-beta.2"
78
+ "@kubb/ast": "5.0.0-beta.21"
85
79
  },
86
80
  "devDependencies": {
87
81
  "@types/react": "^19.2.14",
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
  /**
@@ -26,7 +26,7 @@ export class Runtime {
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>): CodeNode[] {
64
+ const nodes: CodeNode[] = []
65
+ collectCode(props['children'], nodes)
66
+ return nodes
67
+ }
68
+
69
+ function collectCode(element: unknown, nodes: 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: 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 | undefined,
103
+ export: props['export'] as boolean | undefined,
104
+ default: props['default'] as boolean | undefined,
105
+ async: props['async'] as boolean | undefined,
106
+ generics: props['generics'] as string | undefined,
107
+ returnType: props['returnType'] as string | undefined,
108
+ JSDoc: props['JSDoc'] as JSDocNode | 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 | undefined,
120
+ export: props['export'] as boolean | undefined,
121
+ default: props['default'] as boolean | undefined,
122
+ async: props['async'] as boolean | undefined,
123
+ generics: props['generics'] as string | undefined,
124
+ returnType: props['returnType'] as string | undefined,
125
+ singleLine: props['singleLine'] as boolean | undefined,
126
+ JSDoc: props['JSDoc'] as JSDocNode | 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 | undefined,
138
+ export: props['export'] as boolean | undefined,
139
+ asConst: props['asConst'] as boolean | undefined,
140
+ JSDoc: props['JSDoc'] as JSDocNode | 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 | undefined,
152
+ JSDoc: props['JSDoc'] as JSDocNode | undefined,
153
+ nodes: collectCodeNodes(props),
154
+ }),
155
+ )
156
+ return
157
+ }
158
+ }
159
+
160
+ type FileChildren = { sources: SourceNode[]; exports: ExportNode[]; imports: ImportNode[] }
161
+
162
+ function collectFileChildren(element: unknown): FileChildren {
163
+ const sources: SourceNode[] = []
164
+ const exports: ExportNode[] = []
165
+ const imports: 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 | 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: 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
+ }
package/src/constants.ts CHANGED
@@ -5,20 +5,30 @@ import type { ElementNames } from './types.ts'
5
5
  */
6
6
  export const TEXT_NODE_NAME = '#text' as const
7
7
 
8
+ export const KUBB_FILE = 'kubb-file' as const
9
+ export const KUBB_SOURCE = 'kubb-source' as const
10
+ export const KUBB_EXPORT = 'kubb-export' as const
11
+ export const KUBB_IMPORT = 'kubb-import' as const
12
+ export const KUBB_FUNCTION = 'kubb-function' as const
13
+ export const KUBB_ARROW_FUNCTION = 'kubb-arrow-function' as const
14
+ export const KUBB_CONST = 'kubb-const' as const
15
+ export const KUBB_TYPE = 'kubb-type' as const
16
+ export const KUBB_JSX = 'kubb-jsx' as const
17
+
8
18
  /**
9
19
  * Set of all element names recognized by the Kubb renderer.
10
20
  * Used to distinguish Kubb-owned elements from unrecognized or text nodes during tree traversal.
11
21
  */
12
22
  export const nodeNames = new Set<ElementNames>([
13
- 'kubb-export',
14
- 'kubb-file',
15
- 'kubb-source',
16
- 'kubb-import',
17
- 'kubb-function',
18
- 'kubb-arrow-function',
19
- 'kubb-const',
20
- 'kubb-type',
21
- 'kubb-jsx',
23
+ KUBB_EXPORT,
24
+ KUBB_FILE,
25
+ KUBB_SOURCE,
26
+ KUBB_IMPORT,
27
+ KUBB_FUNCTION,
28
+ KUBB_ARROW_FUNCTION,
29
+ KUBB_CONST,
30
+ KUBB_TYPE,
31
+ KUBB_JSX,
22
32
  'kubb-text',
23
33
  'kubb-root',
24
34
  'kubb-app',
@@ -1,93 +1,90 @@
1
1
  import type { FileNode } from '@kubb/ast'
2
2
  import { Runtime } from './Runtime.tsx'
3
+ import { SyncRuntime } from './SyncRuntime.tsx'
3
4
  import type { KubbReactElement } from './types.ts'
4
5
 
5
- type Options = {
6
- /**
7
- * Print each render result to the console for debugging.
8
- * Useful when diagnosing output differences between renders.
9
- * @default false
10
- */
11
- debug?: boolean
12
- }
13
-
14
6
  /**
15
- * The renderer instance returned by {@link createRenderer}.
16
- */
17
- type Renderer = {
18
- /**
19
- * Render a JSX element tree and collect the resulting {@link FileNode} entries.
20
- * Resolves once all synchronous render work (including React's flush) is done.
21
- */
22
- render(Element: KubbReactElement): Promise<void>
23
- /**
24
- * Tear down the renderer and release all React resources.
25
- * Pass an `Error` to signal an abnormal shutdown.
26
- */
27
- unmount(error?: Error | number | null): void
28
- /**
29
- * The {@link FileNode} entries collected from the most recent `render` call.
30
- */
31
- files: Array<FileNode>
32
- }
33
-
34
- /**
35
- * Create a Kubb JSX renderer.
7
+ * Renderer factory for generators that produce JSX output.
36
8
  *
37
- * The renderer converts a React JSX element tree built from the components in this
38
- * package into an array of {@link FileNode} entries representing the generated files.
9
+ * Pass as the `renderer` property of `defineGenerator`. Core drives rendering
10
+ * without a hard dependency on `@kubb/renderer-jsx`.
39
11
  *
40
- * @example Basic usage
12
+ * @example
41
13
  * ```ts
42
- * import { createRenderer, File } from '@kubb/renderer-jsx'
14
+ * import { jsxRenderer } from '@kubb/renderer-jsx'
43
15
  *
44
- * const renderer = createRenderer()
45
- * await renderer.render(
46
- * <File baseName="pet.ts" path="src/models/pet.ts">
47
- * <File.Source name="Pet" isExportable isIndexable>
48
- * {`export type Pet = { id: number; name: string }`}
49
- * </File.Source>
50
- * </File>
51
- * )
52
- * console.log(renderer.files) // [FileNode]
53
- * renderer.unmount()
16
+ * export const myGenerator = defineGenerator<PluginTs>({
17
+ * renderer: jsxRenderer,
18
+ * schema(node, options) {
19
+ * return <File baseName="output.ts" path="src/output.ts">...</File>
20
+ * },
21
+ * })
54
22
  * ```
55
23
  */
56
- export function createRenderer(options: Options = {}): Renderer {
57
- const runtime = new Runtime(options)
58
-
24
+ export const jsxRenderer = () => {
25
+ const runtime = new Runtime()
59
26
  return {
60
- async render(Element) {
61
- await runtime.render(Element)
27
+ async render(element: KubbReactElement) {
28
+ await runtime.render(element)
62
29
  },
63
30
  get files() {
64
31
  return runtime.nodes
65
32
  },
66
- unmount(error) {
33
+ dispose() {
34
+ runtime.unmount()
35
+ },
36
+ unmount(error?: Error | number | null) {
67
37
  runtime.unmount(error)
68
38
  },
39
+ [Symbol.dispose]() {
40
+ this.dispose()
41
+ },
69
42
  }
70
43
  }
71
44
 
72
45
  /**
73
- * A renderer factory for generators that produce JSX output.
46
+ * Lightweight renderer factory with no React fiber, scheduler, or work loop.
74
47
  *
75
- * Pass this as the `renderer` property of a `defineGenerator` call so that
76
- * core can render the JSX element tree returned by your generator methods
77
- * without a hard dependency on `@kubb/renderer-jsx`.
48
+ * Walks the JSX element tree in a single recursive pass. All components must be
49
+ * pure functions; hooks and class components are not supported. Drop-in
50
+ * replacement for {@link jsxRenderer} at approximately 2–4× the speed.
78
51
  *
79
- * @example
52
+ * @example Drop-in replacement
80
53
  * ```ts
81
- * import { jsxRenderer } from '@kubb/renderer-jsx'
82
- * import { defineGenerator } from '@kubb/core'
54
+ * import { jsxRendererSync } from '@kubb/renderer-jsx'
83
55
  *
84
56
  * export const myGenerator = defineGenerator<PluginTs>({
85
- * name: 'my-generator',
86
- * renderer: jsxRenderer,
57
+ * renderer: jsxRendererSync,
87
58
  * schema(node, options) {
88
59
  * return <File baseName="output.ts" path="src/output.ts">...</File>
89
60
  * },
90
61
  * })
91
62
  * ```
63
+ *
64
+ * @example Stream files as they are produced
65
+ * ```ts
66
+ * for await (const file of jsxRendererSync().stream(element)) {
67
+ * await writeFile(file)
68
+ * }
69
+ * ```
92
70
  */
93
- export const jsxRenderer: () => Renderer = () => createRenderer()
71
+ export const jsxRendererSync = () => {
72
+ const runtime = new SyncRuntime()
73
+
74
+ return {
75
+ async render(element: KubbReactElement): Promise<void> {
76
+ runtime.render(element)
77
+ },
78
+ get files() {
79
+ return runtime.nodes
80
+ },
81
+ stream(element: KubbReactElement): Generator<FileNode> {
82
+ return runtime.stream(element)
83
+ },
84
+ dispose() {},
85
+ unmount(_error?: Error | number | null) {},
86
+ [Symbol.dispose]() {
87
+ this.dispose()
88
+ },
89
+ }
90
+ }