@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.
- package/README.md +124 -0
- package/dist/index.cjs +614 -185
- package/dist/index.d.ts +322 -84
- package/dist/index.js +608 -185
- package/dist/jsx-dev-runtime.d.ts +2 -2
- package/dist/{jsx-namespace-CNp0arTN.d.ts → jsx-namespace-D_TStJE_.d.ts} +2 -2
- package/dist/jsx-runtime.d.ts +2 -2
- package/dist/{types-nAFMiWFw.d.ts → types-Cur_nGCL.d.ts} +9 -8
- package/dist/types.d.ts +1 -1
- package/package.json +5 -10
- package/src/Renderer.ts +1 -5
- package/src/Runtime.tsx +7 -18
- package/src/SyncRuntime.tsx +309 -0
- package/src/components/Callout.tsx +59 -0
- package/src/components/CodeBlock.tsx +37 -0
- package/src/components/Const.tsx +4 -4
- package/src/components/File.tsx +6 -4
- package/src/components/Frontmatter.tsx +38 -0
- package/src/components/Function.tsx +8 -8
- package/src/components/Heading.tsx +34 -0
- package/src/components/List.tsx +40 -0
- package/src/components/Paragraph.tsx +28 -0
- package/src/components/Type.tsx +2 -2
- package/src/constants.ts +19 -9
- package/src/createRenderer.tsx +82 -63
- package/src/dom.ts +7 -19
- package/src/index.ts +7 -1
- package/src/types.ts +8 -7
- package/src/utils.ts +153 -174
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/jsx-dev-runtime.cjs.map +0 -1
- package/dist/jsx-dev-runtime.js.map +0 -1
- package/dist/jsx-runtime-Cvu_ZYgL.js.map +0 -1
- package/dist/jsx-runtime-DdmO3p0U.cjs.map +0 -1
- package/dist/jsx-runtime.cjs.map +0 -1
- package/dist/jsx-runtime.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as __name } from "./chunk-Bb7HlUDG.js";
|
|
2
|
-
import { h as KubbReactNode, m as KubbReactElement } from "./types-
|
|
3
|
-
import { t as JSX } from "./jsx-namespace-
|
|
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-
|
|
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-
|
|
39
|
+
//# sourceMappingURL=jsx-namespace-D_TStJE_.d.ts.map
|
package/dist/jsx-runtime.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as __name } from "./chunk-Bb7HlUDG.js";
|
|
2
|
-
import { h as KubbReactNode, m as KubbReactElement } from "./types-
|
|
3
|
-
import { t as JSX } from "./jsx-namespace-
|
|
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 |
|
|
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:
|
|
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-
|
|
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-
|
|
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.
|
|
4
|
-
"description": "
|
|
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
|
-
"
|
|
78
|
+
"yaml": "^2.9.0",
|
|
79
|
+
"@kubb/ast": "5.0.0-beta.31"
|
|
85
80
|
},
|
|
86
81
|
"devDependencies": {
|
|
87
|
-
"@types/react": "^19.2.
|
|
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:
|
|
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 {
|
|
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 '
|
|
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 {
|
|
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 =
|
|
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 =
|
|
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'
|
package/src/components/Const.tsx
CHANGED
|
@@ -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
|
*/
|
package/src/components/File.tsx
CHANGED
|
@@ -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
|
*/
|