@kubb/renderer-jsx 5.0.0-beta.62 → 5.0.0-beta.64

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,298 +0,0 @@
1
- import type { ArrowFunctionNode, CodeNode, ExportNode, FileNode, ImportNode, JSDocNode, SourceNode } from '@kubb/ast'
2
- import * as factory from '@kubb/ast/factory'
3
- import { KUBB_ARROW_FUNCTION, KUBB_CONST, KUBB_EXPORT, KUBB_FILE, KUBB_FUNCTION, KUBB_IMPORT, KUBB_JSX, KUBB_SOURCE, KUBB_TYPE } from './constants.ts'
4
- import { Fragment } from './jsx-runtime.ts'
5
- import type { KubbReactElement } from './types.ts'
6
-
7
- type OnText = (text: string) => void
8
- type OnHost = (type: string, props: Record<string, unknown>) => void
9
-
10
- /**
11
- * Walks `element`, resolving arrays, Fragments, and function components
12
- * transparently, then calls `onText` for primitive values and `onHost` for
13
- * every host element encountered. Pure function components are called
14
- * synchronously. Hooks and class components are not supported.
15
- */
16
- function walkElement(element: unknown, onText: OnText, onHost: OnHost): void {
17
- if (element == null || typeof element === 'boolean') return
18
-
19
- if (typeof element === 'string' || typeof element === 'number' || typeof element === 'bigint') {
20
- onText(String(element))
21
- return
22
- }
23
-
24
- if (Array.isArray(element)) {
25
- for (const child of element) walkElement(child, onText, onHost)
26
- return
27
- }
28
-
29
- if (typeof element === 'object' && '$$typeof' in element) {
30
- const el = element as unknown as KubbReactElement
31
- const { type } = el
32
- const props = el.props as Record<string, unknown>
33
-
34
- if (type === Fragment) {
35
- walkElement(props['children'], onText, onHost)
36
- return
37
- }
38
- if (typeof type === 'function') {
39
- walkElement((type as (p: unknown) => unknown)(props), onText, onHost)
40
- return
41
- }
42
- if (typeof type === 'string') {
43
- onHost(type, props)
44
- }
45
- }
46
- }
47
-
48
- function toBool(val: unknown): boolean {
49
- return (val ?? false) as boolean
50
- }
51
-
52
- function collectCodeNodes(props: Record<string, unknown>): Array<CodeNode> {
53
- const nodes: Array<CodeNode> = []
54
- collectCode(props['children'], nodes)
55
- return nodes
56
- }
57
-
58
- function collectCode(element: unknown, nodes: Array<CodeNode>): void {
59
- walkElement(
60
- element,
61
- (text) => {
62
- if (text.trim()) nodes.push(factory.createText(text))
63
- },
64
- (type, props) => resolveCodeNode(type, props, nodes),
65
- )
66
- }
67
-
68
- function resolveCodeNode(type: string, props: Record<string, unknown>, nodes: Array<CodeNode>): void {
69
- if (type === 'br') {
70
- nodes.push(factory.createBreak())
71
- return
72
- }
73
-
74
- if (type === KUBB_JSX) {
75
- let value = ''
76
- walkElement(
77
- props['children'],
78
- (t) => {
79
- value += t
80
- },
81
- () => {},
82
- )
83
- if (value) nodes.push(factory.createJsx(value))
84
- return
85
- }
86
-
87
- if (type === KUBB_FUNCTION) {
88
- nodes.push(
89
- factory.createFunction({
90
- name: props['name'] as string,
91
- params: props['params'] as string | null | undefined,
92
- export: props['export'] as boolean | null | undefined,
93
- default: props['default'] as boolean | null | undefined,
94
- async: props['async'] as boolean | null | undefined,
95
- generics: props['generics'] as string | Array<string> | null | undefined,
96
- returnType: props['returnType'] as string | null | undefined,
97
- JSDoc: props['JSDoc'] as JSDocNode | null | undefined,
98
- nodes: collectCodeNodes(props),
99
- }),
100
- )
101
- return
102
- }
103
-
104
- if (type === KUBB_ARROW_FUNCTION) {
105
- nodes.push(
106
- factory.createArrowFunction({
107
- name: props['name'] as string,
108
- params: props['params'] as string | null | undefined,
109
- export: props['export'] as boolean | null | undefined,
110
- default: props['default'] as boolean | null | undefined,
111
- async: props['async'] as boolean | null | undefined,
112
- generics: props['generics'] as string | Array<string> | null | undefined,
113
- returnType: props['returnType'] as string | null | undefined,
114
- singleLine: props['singleLine'] as boolean | null | undefined,
115
- JSDoc: props['JSDoc'] as JSDocNode | null | undefined,
116
- nodes: collectCodeNodes(props),
117
- } as Omit<ArrowFunctionNode, 'kind'>),
118
- )
119
- return
120
- }
121
-
122
- if (type === KUBB_CONST) {
123
- nodes.push(
124
- factory.createConst({
125
- name: props['name'] as string,
126
- type: props['type'] as string | null | undefined,
127
- export: props['export'] as boolean | null | undefined,
128
- asConst: props['asConst'] as boolean | null | undefined,
129
- JSDoc: props['JSDoc'] as JSDocNode | null | undefined,
130
- nodes: collectCodeNodes(props),
131
- }),
132
- )
133
- return
134
- }
135
-
136
- if (type === KUBB_TYPE) {
137
- nodes.push(
138
- factory.createType({
139
- name: props['name'] as string,
140
- export: props['export'] as boolean | null | undefined,
141
- JSDoc: props['JSDoc'] as JSDocNode | null | undefined,
142
- nodes: collectCodeNodes(props),
143
- }),
144
- )
145
- return
146
- }
147
- }
148
-
149
- type FileChildren = { sources: Array<SourceNode>; exports: Array<ExportNode>; imports: Array<ImportNode> }
150
-
151
- function collectFileChildren(element: unknown): FileChildren {
152
- const sources: Array<SourceNode> = []
153
- const exports: Array<ExportNode> = []
154
- const imports: Array<ImportNode> = []
155
-
156
- walkElement(
157
- element,
158
- (text) => {
159
- if (text.trim()) {
160
- throw new Error(`[react] '${text}' should be part of <File.Source> component when using the <File/> component`)
161
- }
162
- },
163
- (type, props) => {
164
- if (type === KUBB_SOURCE) {
165
- sources.push(
166
- factory.createSource({
167
- name: props['name']?.toString(),
168
- isTypeOnly: toBool(props['isTypeOnly']),
169
- isExportable: toBool(props['isExportable']),
170
- isIndexable: toBool(props['isIndexable']),
171
- nodes: collectCodeNodes(props),
172
- }),
173
- )
174
- return
175
- }
176
-
177
- if (type === KUBB_EXPORT) {
178
- exports.push(
179
- factory.createExport({
180
- name: props['name'] as ExportNode['name'],
181
- path: props['path'] as string,
182
- isTypeOnly: toBool(props['isTypeOnly']),
183
- asAlias: toBool(props['asAlias']),
184
- }),
185
- )
186
- return
187
- }
188
-
189
- if (type === KUBB_IMPORT) {
190
- imports.push(
191
- factory.createImport({
192
- name: props['name'] as ImportNode['name'],
193
- path: props['path'] as string,
194
- root: props['root'] as string | null | undefined,
195
- isTypeOnly: toBool(props['isTypeOnly']),
196
- isNameSpace: toBool(props['isNameSpace']),
197
- }),
198
- )
199
- return
200
- }
201
-
202
- const nested = collectFileChildren(props['children'])
203
- sources.push(...nested.sources)
204
- exports.push(...nested.exports)
205
- imports.push(...nested.imports)
206
- },
207
- )
208
-
209
- return { sources, exports, imports }
210
- }
211
-
212
- function* walkFiles(element: unknown): Generator<FileNode> {
213
- if (element == null || typeof element === 'boolean') return
214
-
215
- if (typeof element === 'string' || typeof element === 'number' || typeof element === 'bigint') return
216
-
217
- if (Array.isArray(element)) {
218
- for (const child of element) yield* walkFiles(child)
219
- return
220
- }
221
-
222
- if (typeof element === 'object' && '$$typeof' in element) {
223
- const el = element as unknown as KubbReactElement
224
- const { type } = el
225
- const props = el.props as Record<string, unknown>
226
-
227
- if (type === Fragment) {
228
- yield* walkFiles(props['children'])
229
- return
230
- }
231
-
232
- if (typeof type === 'function') {
233
- yield* walkFiles((type as (p: unknown) => unknown)(props))
234
- return
235
- }
236
-
237
- if (typeof type === 'string') {
238
- if (type === KUBB_FILE && props['baseName'] !== undefined && props['path'] !== undefined) {
239
- const { sources, exports, imports } = collectFileChildren(props['children'])
240
- yield {
241
- baseName: props['baseName'],
242
- path: props['path'],
243
- meta: props['meta'] || {},
244
- footer: props['footer'],
245
- banner: props['banner'],
246
- sources,
247
- exports,
248
- imports,
249
- } as FileNode
250
- } else {
251
- yield* walkFiles(props['children'])
252
- }
253
- }
254
- }
255
- }
256
-
257
- /**
258
- * Synchronous JSX renderer that walks the element tree in a single pass,
259
- * producing {@link FileNode} objects directly without an intermediate virtual
260
- * DOM. No React fiber, scheduler, or work loop is involved.
261
- *
262
- * All components must be pure functions. Hooks and class components are not
263
- * supported. Produces identical output to the React-backed {@link Runtime} at
264
- * approximately 2, 4× the speed and a fraction of the allocations.
265
- */
266
- export class SyncRuntime {
267
- /**
268
- * Accumulated {@link FileNode} results from every {@link render} call.
269
- */
270
- nodes: Array<FileNode> = []
271
-
272
- /**
273
- * Walks `element` synchronously, converts every `<kubb-file>` subtree into
274
- * a {@link FileNode} with no intermediate virtual DOM, and appends the results
275
- * to {@link nodes}.
276
- */
277
- render(element: KubbReactElement): void {
278
- for (const file of walkFiles(element)) {
279
- this.nodes.push(file)
280
- }
281
- }
282
-
283
- /**
284
- * Walks `element` synchronously and yields each {@link FileNode} as it is
285
- * produced, without buffering into an intermediate array first. Callers can
286
- * begin processing each file before the rest of the element tree is traversed.
287
- *
288
- * @example
289
- * ```ts
290
- * for (const file of runtime.stream(element)) {
291
- * await writeFile(file)
292
- * }
293
- * ```
294
- */
295
- *stream(element: KubbReactElement): Generator<FileNode> {
296
- yield* walkFiles(element)
297
- }
298
- }
@@ -1,59 +0,0 @@
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'
@@ -1,72 +0,0 @@
1
- import type { JSDoc, Key, KubbReactElement, KubbReactNode } from '../types.ts'
2
-
3
- type ConstProps = {
4
- key?: Key
5
- /**
6
- * Identifier of the generated constant declaration.
7
- *
8
- * @example
9
- * `name: 'petSchema'`
10
- */
11
- name: string
12
- /**
13
- * Emit the `export` keyword before the `const` declaration.
14
- * - `true` generates `export const name = …`
15
- * - `false` generates `const name = …`
16
- * @default false
17
- */
18
- export?: boolean | null
19
- /**
20
- * TypeScript type annotation for the constant, written verbatim after `const name:`.
21
- *
22
- * @example
23
- * `type: 'Pet'` → `const pet: Pet = …`
24
- */
25
- type?: string | null
26
- /**
27
- * JSDoc block to prepend to the constant declaration.
28
- * Each entry in `comments` becomes one line inside the emitted `/** … *\/` block.
29
- */
30
- JSDoc?: JSDoc | null
31
- /**
32
- * Append `as const` after the initialiser, enabling TypeScript const assertions.
33
- * - `true` generates `const name = … as const`
34
- * - `false` generates `const name = …`
35
- * @default false
36
- */
37
- asConst?: boolean | null
38
- /**
39
- * Child nodes rendered as the initialiser expression of the constant.
40
- */
41
- children?: KubbReactNode
42
- }
43
-
44
- /**
45
- * Generates a TypeScript constant declaration.
46
- *
47
- * @example Named export with type annotation
48
- * ```tsx
49
- * <Const export name="petSchema" type="z.ZodType<Pet>">
50
- * {`z.object({ id: z.number() })`}
51
- * </Const>
52
- * // export const petSchema: z.ZodType<Pet> = z.object({ id: z.number() })
53
- * ```
54
- *
55
- * @example With JSDoc and const assertion
56
- * ```tsx
57
- * <Const name="HTTP_METHODS" asConst JSDoc={{ comments: ['@description Supported HTTP methods.'] }}>
58
- * {`['GET', 'POST', 'PUT', 'DELETE']`}
59
- * </Const>
60
- * ```
61
- */
62
- export function Const({ children, ...props }: ConstProps): KubbReactElement {
63
- const { name, export: canExport, type, JSDoc, asConst } = props
64
-
65
- return (
66
- <kubb-const name={name} type={type} export={canExport} asConst={asConst} JSDoc={JSDoc}>
67
- {children}
68
- </kubb-const>
69
- )
70
- }
71
-
72
- Const.displayName = 'Const'
@@ -1,188 +0,0 @@
1
- import type { ExportNode, ImportNode, SourceNode } from '@kubb/ast'
2
- import type { Key, KubbReactElement, KubbReactNode } from '../types.ts'
3
-
4
- type BasePropsWithBaseName = {
5
- /**
6
- * Base file name including extension, derived from the input path.
7
- * Based on UNIX basename convention: `${name}${extname}`.
8
- *
9
- * @link https://nodejs.org/api/path.html#pathbasenamepath-suffix
10
- *
11
- * @example
12
- * `baseName: 'petStore.ts'`
13
- */
14
- baseName: `${string}.${string}`
15
- /**
16
- * Fully qualified path to the generated file.
17
- *
18
- * @example
19
- * `path: 'src/models/petStore.ts'`
20
- */
21
- path: string
22
- }
23
-
24
- type BasePropsWithoutBaseName = {
25
- baseName?: never
26
- /**
27
- * Fully qualified path to the generated file.
28
- * Optional when `baseName` is omitted, the component renders its children inline.
29
- */
30
- path?: string | null
31
- }
32
-
33
- type BaseProps = BasePropsWithBaseName | BasePropsWithoutBaseName
34
-
35
- type Props<TMeta> = BaseProps & {
36
- key?: Key
37
- /**
38
- * Arbitrary metadata attached to the file node.
39
- * Used by plugins for barrel generation and custom post-processing.
40
- */
41
- meta?: TMeta | null
42
- /**
43
- * Text prepended to the generated file content before any source blocks.
44
- * Accepts `null` so `resolver.resolveBanner()` results can be passed directly.
45
- */
46
- banner?: string | null
47
- /**
48
- * Text appended to the generated file content after all source blocks.
49
- * Accepts `null` so `resolver.resolveFooter()` results can be passed directly.
50
- */
51
- footer?: string | null
52
- /**
53
- * Child nodes rendered as the content of this file (source blocks, imports, exports).
54
- */
55
- children?: KubbReactNode
56
- }
57
-
58
- /**
59
- * Declares a generated file entry to be collected by the renderer.
60
- *
61
- * When both `baseName` and `path` are provided, the component registers a new
62
- * `FileNode` and passes its children through as source blocks.
63
- * When either is omitted the children are rendered inline without creating a file entry.
64
- *
65
- * @example Basic file with a source block
66
- * ```tsx
67
- * <File baseName="petStore.ts" path="src/models/petStore.ts">
68
- * <File.Source name="Pet" isExportable isIndexable>
69
- * {`export type Pet = { id: number; name: string }`}
70
- * </File.Source>
71
- * </File>
72
- * ```
73
- */
74
- export function File<TMeta extends object = object>({ children, ...props }: Props<TMeta>): KubbReactElement {
75
- const { baseName, path } = props
76
-
77
- if (!baseName || !path) {
78
- return <>{children}</>
79
- }
80
-
81
- return <kubb-file {...props}>{children}</kubb-file>
82
- }
83
-
84
- File.displayName = 'File'
85
-
86
- type FileSourceProps = Omit<SourceNode, 'kind' | 'value'> & {
87
- key?: Key
88
- /**
89
- * Child nodes rendered as the source content of this block.
90
- */
91
- children?: KubbReactNode
92
- }
93
-
94
- /**
95
- * Marks a block of source text to be associated with the enclosing {@link File}.
96
- *
97
- * Children are treated as the source string. When `isExportable` is `true` the
98
- * `name` is used for deduplication and barrel generation.
99
- *
100
- * @example Exportable, indexable source block
101
- * ```tsx
102
- * <File.Source name="Pet" isExportable isIndexable>
103
- * {`export type Pet = { id: number; name: string }`}
104
- * </File.Source>
105
- * ```
106
- *
107
- * @example Type-only source block
108
- * ```tsx
109
- * <File.Source name="PetId" isTypeOnly isExportable>
110
- * {`export type PetId = string`}
111
- * </File.Source>
112
- * ```
113
- */
114
- function FileSource({ children, ...props }: FileSourceProps): KubbReactElement {
115
- const { name, isExportable, isIndexable, isTypeOnly } = props
116
-
117
- return (
118
- <kubb-source name={name} isTypeOnly={isTypeOnly} isExportable={isExportable} isIndexable={isIndexable}>
119
- {children}
120
- </kubb-source>
121
- )
122
- }
123
-
124
- FileSource.displayName = 'FileSource'
125
-
126
- type FileExportProps = Omit<ExportNode, 'kind'> & { key?: Key }
127
-
128
- /**
129
- * Declares an export entry for the enclosing {@link File}.
130
- *
131
- * The export is collected by the renderer and emitted at the top of the generated file.
132
- *
133
- * @example Named export
134
- * ```tsx
135
- * <File.Export name={['Pet']} path="./models/petStore" />
136
- * // export { Pet } from './models/petStore'
137
- * ```
138
- *
139
- * @example Type-only wildcard export
140
- * ```tsx
141
- * <File.Export path="./models/petStore" isTypeOnly />
142
- * // export type * from './models/petStore'
143
- * ```
144
- */
145
- function FileExport(props: FileExportProps): KubbReactElement {
146
- const { name, path, isTypeOnly, asAlias } = props
147
-
148
- return <kubb-export name={name} path={path} isTypeOnly={isTypeOnly} asAlias={asAlias} />
149
- }
150
-
151
- FileExport.displayName = 'FileExport'
152
-
153
- type FileImportProps = Omit<ImportNode, 'kind'> & { key?: Key }
154
-
155
- /**
156
- * Declares an import entry for the enclosing {@link File}.
157
- *
158
- * The import is collected by the renderer and emitted at the top of the generated file.
159
- *
160
- * @example Named import
161
- * ```tsx
162
- * <File.Import name={['useState']} path="react" />
163
- * // import { useState } from 'react'
164
- * ```
165
- *
166
- * @example Type-only import
167
- * ```tsx
168
- * <File.Import name={['Pet']} path="./models" isTypeOnly />
169
- * // import type { Pet } from './models'
170
- * ```
171
- *
172
- * @example Namespace import
173
- * ```tsx
174
- * <File.Import name="z" path="zod" isNameSpace />
175
- * // import * as z from 'zod'
176
- * ```
177
- */
178
- function FileImport(props: FileImportProps): KubbReactElement {
179
- const { name, root, path, isTypeOnly, isNameSpace } = props
180
-
181
- return <kubb-import name={name} root={root} path={path} isNameSpace={isNameSpace} isTypeOnly={isTypeOnly} />
182
- }
183
-
184
- FileImport.displayName = 'FileImport'
185
-
186
- File.Export = FileExport
187
- File.Import = FileImport
188
- File.Source = FileSource
@@ -1,38 +0,0 @@
1
- import { stringify } from 'yaml'
2
- import type { Key, KubbReactElement } from '../types.ts'
3
-
4
- type Props = {
5
- key?: Key
6
- /**
7
- * Plain object serialized 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'