@kubb/renderer-jsx 5.0.0-alpha.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/LICENSE +14 -0
  2. package/dist/chunk-CErwXX-a.js +28 -0
  3. package/dist/index.cjs +18090 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.ts +330 -0
  6. package/dist/index.js +18075 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/jsx-dev-runtime.cjs +11 -0
  9. package/dist/jsx-dev-runtime.cjs.map +1 -0
  10. package/dist/jsx-dev-runtime.d.ts +14 -0
  11. package/dist/jsx-dev-runtime.js +10 -0
  12. package/dist/jsx-dev-runtime.js.map +1 -0
  13. package/dist/jsx-namespace-Cx1KMEbe.d.ts +39 -0
  14. package/dist/jsx-runtime-CeMde2cR.cjs +1503 -0
  15. package/dist/jsx-runtime-CeMde2cR.cjs.map +1 -0
  16. package/dist/jsx-runtime-Dmf9wTKR.js +1448 -0
  17. package/dist/jsx-runtime-Dmf9wTKR.js.map +1 -0
  18. package/dist/jsx-runtime.cjs +15 -0
  19. package/dist/jsx-runtime.cjs.map +1 -0
  20. package/dist/jsx-runtime.d.ts +16 -0
  21. package/dist/jsx-runtime.js +12 -0
  22. package/dist/jsx-runtime.js.map +1 -0
  23. package/dist/types-C7FD9BLg.d.ts +119 -0
  24. package/dist/types.cjs +0 -0
  25. package/dist/types.d.ts +2 -0
  26. package/dist/types.js +1 -0
  27. package/package.json +121 -0
  28. package/src/Renderer.ts +184 -0
  29. package/src/Runtime.tsx +171 -0
  30. package/src/components/Const.tsx +44 -0
  31. package/src/components/File.tsx +106 -0
  32. package/src/components/Function.tsx +107 -0
  33. package/src/components/Jsx.tsx +34 -0
  34. package/src/components/Root.tsx +64 -0
  35. package/src/components/Type.tsx +40 -0
  36. package/src/context/KubbContext.ts +15 -0
  37. package/src/context/OasContext.ts +9 -0
  38. package/src/createRenderer.tsx +32 -0
  39. package/src/dom.ts +91 -0
  40. package/src/globals.ts +34 -0
  41. package/src/index.ts +10 -0
  42. package/src/jsx-dev-runtime.ts +10 -0
  43. package/src/jsx-namespace.d.ts +53 -0
  44. package/src/jsx-runtime.ts +12 -0
  45. package/src/types.ts +113 -0
  46. package/src/utils.ts +301 -0
@@ -0,0 +1,184 @@
1
+ import { createContext } from 'react'
2
+ import Reconciler, { type ReactContext } from 'react-reconciler'
3
+ import { DefaultEventPriority, NoEventPriority } from 'react-reconciler/constants.js'
4
+ import { appendChildNode, createNode, createTextNode, insertBeforeNode, removeChildNode, setAttribute, setTextNodeValue } from './dom.ts'
5
+ import type { KubbReactNode } from './types'
6
+ import type { DOMElement, DOMNodeAttribute, ElementNames, TextNode } from './types.ts'
7
+
8
+ declare module 'react-reconciler' {
9
+ // @ts-expect-error custom override
10
+ interface Reconciler {
11
+ updateContainerSync(element: KubbReactNode, container: unknown, parentComponent: any, callback?: null | (() => void)): void
12
+ flushSyncWork(): void
13
+ createContainer(
14
+ containerInfo: unknown,
15
+ tag: Reconciler.RootTag,
16
+ hydrationCallbacks: null | Reconciler.SuspenseHydrationCallbacks<any>,
17
+ isStrictMode: boolean,
18
+ concurrentUpdatesByDefaultOverride: null | boolean,
19
+ identifierPrefix: string,
20
+ onUncaughtError: (error: Error) => void,
21
+ onCaughtError: (error: Error) => void,
22
+ onRecoverableError: (error: Error) => void,
23
+ transitionCallbacks: null | Reconciler.TransitionTracingCallbacks,
24
+ ): Reconciler.OpaqueRoot
25
+ }
26
+ }
27
+
28
+ type Props = Record<string, unknown>
29
+
30
+ type HostContext = {
31
+ type: ElementNames
32
+ isFile: boolean
33
+ isSource: boolean
34
+ }
35
+
36
+ let currentUpdatePriority = NoEventPriority
37
+
38
+ /**
39
+ * @link https://www.npmjs.com/package/react-devtools-inline
40
+ * @link https://github.com/nitin42/Making-a-custom-React-renderer/blob/master/part-one.md
41
+ * @link https://github.com/facebook/react/tree/main/packages/react-reconciler#practical-examples
42
+ * @link https://github.com/vadimdemedes/ink
43
+ * @link https://github.com/pixijs/pixi-react/tree/main/packages
44
+ * @link https://github.com/diegomura/react-pdf/blob/master/packages/reconciler/src/reconciler-31.ts
45
+ */
46
+ export const Renderer = Reconciler({
47
+ getRootHostContext: () => ({
48
+ type: 'kubb-root',
49
+ isFile: false,
50
+ isSource: false,
51
+ }),
52
+ prepareForCommit: () => {
53
+ return null
54
+ },
55
+ preparePortalMount: () => null,
56
+ clearContainer: () => false,
57
+ resetAfterCommit(rootNode: DOMElement) {
58
+ if (typeof rootNode.onRender === 'function') {
59
+ rootNode.onRender()
60
+ }
61
+ },
62
+ getChildHostContext(parentHostContext: HostContext, type: ElementNames) {
63
+ const isInsideText = type === 'kubb-text'
64
+ const isFile = type === 'kubb-file' || parentHostContext.isFile
65
+ const isSource = type === 'kubb-source' || parentHostContext.isSource
66
+
67
+ return { isInsideText, isFile, isSource, type }
68
+ },
69
+ shouldSetTextContent: () => false,
70
+ createInstance(originalType: ElementNames, newProps: Props, _root: DOMElement) {
71
+ const node = createNode(originalType)
72
+
73
+ for (const [key, value] of Object.entries(newProps)) {
74
+ if (key === 'children') {
75
+ continue
76
+ }
77
+
78
+ // Skip undefined values to match React's behavior
79
+ if (value !== undefined) {
80
+ setAttribute(node, key, value as DOMNodeAttribute)
81
+ }
82
+ }
83
+
84
+ return node
85
+ },
86
+ createTextInstance(text: string, _root: DOMElement, hostContext: HostContext) {
87
+ if (hostContext.isFile && !hostContext.isSource) {
88
+ throw new Error(`[react] '${text}' should be part of <File.Source> component when using the <File/> component`)
89
+ }
90
+
91
+ return createTextNode(text)
92
+ },
93
+ resetTextContent() {},
94
+ hideTextInstance(node: TextNode) {
95
+ setTextNodeValue(node, '')
96
+ },
97
+ unhideTextInstance(node: TextNode, text: string) {
98
+ setTextNodeValue(node, text)
99
+ },
100
+ getPublicInstance: (instance) => instance,
101
+ appendInitialChild: appendChildNode,
102
+ appendChild: appendChildNode,
103
+ insertBefore: insertBeforeNode,
104
+ finalizeInitialChildren(_node, _type, _props, _rootNode) {
105
+ return false
106
+ },
107
+ supportsMutation: true,
108
+ isPrimaryRenderer: true,
109
+ supportsPersistence: false,
110
+ supportsHydration: false,
111
+ scheduleTimeout: setTimeout,
112
+ cancelTimeout: clearTimeout,
113
+ noTimeout: -1,
114
+ beforeActiveInstanceBlur() {},
115
+ afterActiveInstanceBlur() {},
116
+ detachDeletedInstance() {},
117
+ getInstanceFromNode: () => null,
118
+ prepareScopeUpdate() {},
119
+ getInstanceFromScope: () => null,
120
+ appendChildToContainer: appendChildNode,
121
+ insertInContainerBefore: insertBeforeNode,
122
+ removeChildFromContainer(node: DOMElement, removeNode: TextNode) {
123
+ removeChildNode(node, removeNode)
124
+ },
125
+ commitMount() {},
126
+ commitUpdate(node: DOMElement, _payload, _type, _oldProps: Props, newProps: Props) {
127
+ const { props } = newProps
128
+
129
+ if (props) {
130
+ for (const [key, value] of Object.entries(props)) {
131
+ // Skip undefined values to match React's behavior
132
+ if (value !== undefined) {
133
+ setAttribute(node, key, value as DOMNodeAttribute)
134
+ }
135
+ }
136
+ }
137
+ },
138
+ commitTextUpdate(node: TextNode, _oldText, newText) {
139
+ setTextNodeValue(node, newText)
140
+ },
141
+ removeChild(node: DOMElement, removeNode: TextNode) {
142
+ removeChildNode(node, removeNode)
143
+ },
144
+ setCurrentUpdatePriority: (newPriority: number) => {
145
+ currentUpdatePriority = newPriority
146
+ },
147
+ getCurrentUpdatePriority: () => currentUpdatePriority,
148
+ resolveUpdatePriority() {
149
+ if (currentUpdatePriority !== NoEventPriority) {
150
+ return currentUpdatePriority
151
+ }
152
+
153
+ return DefaultEventPriority
154
+ },
155
+ maySuspendCommit() {
156
+ return false
157
+ },
158
+ // eslint-disable-next-line @typescript-eslint/naming-convention
159
+ NotPendingTransition: undefined,
160
+ // eslint-disable-next-line @typescript-eslint/naming-convention
161
+ HostTransitionContext: createContext(null) as unknown as ReactContext<unknown>,
162
+ resetFormInstance() {},
163
+ requestPostPaintCallback() {},
164
+ shouldAttemptEagerTransition() {
165
+ return false
166
+ },
167
+ trackSchedulerEvent() {},
168
+ resolveEventType() {
169
+ return null
170
+ },
171
+ resolveEventTimeStamp() {
172
+ return -1.1
173
+ },
174
+ preloadInstance() {
175
+ return true
176
+ },
177
+ startSuspendingCommit() {},
178
+ suspendInstance() {},
179
+ waitForCommitToBeReady() {
180
+ return null
181
+ },
182
+ })
183
+
184
+ export type { FiberRoot } from 'react-reconciler'
@@ -0,0 +1,171 @@
1
+ import { onProcessExit } from '@internals/utils'
2
+ import type { FileNode } from '@kubb/ast/types'
3
+ import { ConcurrentRoot } from 'react-reconciler/constants.js'
4
+ import { Root } from './components/Root.tsx'
5
+ import { createNode } from './dom.ts'
6
+ import type { FiberRoot } from './Renderer.ts'
7
+ import { Renderer } from './Renderer.ts'
8
+ import type { DOMElement, KubbReactElement } from './types.ts'
9
+ import { processFiles } from './utils.ts'
10
+
11
+ type Options = {
12
+ /**
13
+ * Set this to true to always see the result of the render in the console(line per render)
14
+ */
15
+ debug?: boolean
16
+ }
17
+
18
+ export class Runtime {
19
+ readonly #options: Options
20
+ #isUnmounted: boolean
21
+ #renderError?: Error
22
+
23
+ exitPromise?: Promise<void>
24
+
25
+ nodes: FileNode[] = []
26
+ readonly #container: FiberRoot
27
+ readonly #rootNode: DOMElement
28
+
29
+ constructor(options: Options) {
30
+ this.#options = options
31
+ this.#rootNode = createNode('kubb-root')
32
+ this.#rootNode.onRender = this.onRender
33
+ this.#rootNode.onImmediateRender = this.onRender
34
+ this.#isUnmounted = false
35
+ this.unmount.bind(this)
36
+
37
+ // Intercept noisy React errors
38
+ console.error = (data: string | Error) => {
39
+ const message = typeof data === 'string' ? data : data?.message
40
+ if (
41
+ message?.match(/Encountered two children with the same key/gi) ||
42
+ message?.match(/React will try to recreat/gi) ||
43
+ message?.match(/Each child in a list should have a unique/gi) ||
44
+ message?.match(/The above error occurred in the <KubbErrorBoundary/gi) ||
45
+ message?.match(/A React Element from an older version of React was render/gi)
46
+ ) {
47
+ return
48
+ }
49
+ console.log(data)
50
+ }
51
+
52
+ const logRecoverableError = typeof reportError === 'function' ? reportError : console.error
53
+
54
+ const rootTag = ConcurrentRoot
55
+ this.#container = Renderer.createContainer(
56
+ this.#rootNode,
57
+ rootTag,
58
+ null,
59
+ false,
60
+ false,
61
+ 'id',
62
+ logRecoverableError,
63
+ logRecoverableError,
64
+ logRecoverableError,
65
+ null,
66
+ )
67
+
68
+ // Unmount when process exits
69
+ // Unmount when process exits
70
+ this.unsubscribeExit = onProcessExit((code) => {
71
+ this.unmount(code)
72
+ })
73
+ }
74
+
75
+ #renderPromise: Promise<void> = Promise.resolve()
76
+ resolveExitPromise: () => void = () => {}
77
+ rejectExitPromise: (reason?: Error) => void = () => {}
78
+ unsubscribeExit: () => void = () => {}
79
+
80
+ onRender: () => Promise<void> = () => {
81
+ const previous = this.#renderPromise
82
+
83
+ const task = previous
84
+ .catch(() => {})
85
+ .then(async () => {
86
+ if (this.#isUnmounted) {
87
+ return
88
+ }
89
+
90
+ const files = await processFiles(this.#rootNode)
91
+
92
+ this.nodes.push(...files)
93
+
94
+ if (!this.#options?.debug) {
95
+ return
96
+ }
97
+ })
98
+
99
+ this.#renderPromise = task.catch((error) => {
100
+ this.onError(error as Error)
101
+ })
102
+
103
+ return this.#renderPromise
104
+ }
105
+
106
+ onError(error: Error): void {
107
+ // Store the error to be thrown after render completes
108
+ this.#renderError = error
109
+ }
110
+
111
+ onExit(error?: Error): void {
112
+ setTimeout(() => {
113
+ this.unmount(error)
114
+ }, 0)
115
+ }
116
+
117
+ async render(node: KubbReactElement): Promise<void> {
118
+ const props = {
119
+ onExit: this.onExit.bind(this),
120
+ onError: this.onError.bind(this),
121
+ }
122
+
123
+ const element = <Root {...props}>{node}</Root>
124
+
125
+ Renderer.updateContainerSync(element, this.#container, null, null)
126
+ Renderer.flushSyncWork()
127
+ await this.#renderPromise
128
+
129
+ // Throw any errors that occurred during rendering
130
+ if (this.#renderError) {
131
+ const error = this.#renderError
132
+ this.#renderError = undefined
133
+ throw error
134
+ }
135
+ }
136
+
137
+ unmount(error?: Error | number | null): void {
138
+ if (this.#isUnmounted) {
139
+ return
140
+ }
141
+
142
+ if (this.#options?.debug) {
143
+ console.log('Unmount', error)
144
+ }
145
+
146
+ this.onRender()
147
+ this.unsubscribeExit()
148
+
149
+ this.#isUnmounted = true
150
+
151
+ Renderer.updateContainerSync(null, this.#container, null, null)
152
+
153
+ if (error instanceof Error) {
154
+ this.rejectExitPromise(error)
155
+ return
156
+ }
157
+
158
+ this.resolveExitPromise()
159
+ }
160
+
161
+ async waitUntilExit(): Promise<void> {
162
+ if (!this.exitPromise) {
163
+ this.exitPromise = new Promise((resolve, reject) => {
164
+ this.resolveExitPromise = resolve
165
+ this.rejectExitPromise = reject
166
+ })
167
+ }
168
+
169
+ return this.exitPromise
170
+ }
171
+ }
@@ -0,0 +1,44 @@
1
+ import type { JSDoc, Key, KubbReactElement, KubbReactNode } from '../types.ts'
2
+
3
+ export type ConstProps = {
4
+ key?: Key
5
+ /**
6
+ * Name of the const
7
+ */
8
+ name: string
9
+ /**
10
+ * Does this type need to be exported.
11
+ */
12
+ export?: boolean
13
+ /**
14
+ * Type to make the const being typed
15
+ */
16
+ type?: string
17
+ /**
18
+ * Options for JSdocs.
19
+ */
20
+ JSDoc?: JSDoc
21
+ /**
22
+ * Use of `const` assertions
23
+ */
24
+ asConst?: boolean
25
+ /**
26
+ * Children nodes.
27
+ */
28
+ children?: KubbReactNode
29
+ }
30
+
31
+ /**
32
+ * Generates a TypeScript constant declaration.
33
+ */
34
+ export function Const({ children, ...props }: ConstProps): KubbReactElement {
35
+ const { name, export: canExport, type, JSDoc, asConst } = props
36
+
37
+ return (
38
+ <kubb-const name={name} type={type} export={canExport} asConst={asConst} JSDoc={JSDoc}>
39
+ {children}
40
+ </kubb-const>
41
+ )
42
+ }
43
+
44
+ Const.displayName = 'Const'
@@ -0,0 +1,106 @@
1
+ import type { ExportNode, ImportNode, SourceNode } from '@kubb/ast/types'
2
+ import type { Key, KubbReactElement, KubbReactNode } from '../types.ts'
3
+
4
+ type BasePropsWithBaseName = {
5
+ /**
6
+ * Name to be used to dynamically create the baseName(based on input.path).
7
+ * Based on UNIX basename
8
+ * @link https://nodejs.org/api/path.html#pathbasenamepath-suffix
9
+ */
10
+ baseName: `${string}.${string}`
11
+ /**
12
+ * Path will be full qualified path to a specified file.
13
+ */
14
+ path: string
15
+ }
16
+
17
+ type BasePropsWithoutBaseName = {
18
+ baseName?: never
19
+ /**
20
+ * Path will be full qualified path to a specified file.
21
+ */
22
+ path?: string
23
+ }
24
+
25
+ type BaseProps = BasePropsWithBaseName | BasePropsWithoutBaseName
26
+
27
+ type Props<TMeta> = BaseProps & {
28
+ key?: Key
29
+ meta?: TMeta
30
+ banner?: string
31
+ footer?: string
32
+ children?: KubbReactNode
33
+ }
34
+
35
+ /**
36
+ * Adds files to the FileManager
37
+ */
38
+ export function File<TMeta extends object = object>({ children, ...props }: Props<TMeta>): KubbReactElement {
39
+ const { baseName, path } = props
40
+
41
+ if (!baseName || !path) {
42
+ return <>{children}</>
43
+ }
44
+
45
+ return <kubb-file {...props}>{children}</kubb-file>
46
+ }
47
+
48
+ File.displayName = 'File'
49
+
50
+ type FileSourceProps = Omit<SourceNode, 'kind' | 'value'> & {
51
+ key?: Key
52
+ children?: KubbReactNode
53
+ }
54
+
55
+ /**
56
+ * File.Source
57
+ *
58
+ * Marks a block of source text to be associated with the current file when
59
+ * rendering with the FileCollector. Children are treated as the source string.
60
+ */
61
+ function FileSource({ children, ...props }: FileSourceProps): KubbReactElement {
62
+ const { name, isExportable, isIndexable, isTypeOnly } = props
63
+
64
+ return (
65
+ <kubb-source name={name} isTypeOnly={isTypeOnly} isExportable={isExportable} isIndexable={isIndexable}>
66
+ {children}
67
+ </kubb-source>
68
+ )
69
+ }
70
+
71
+ FileSource.displayName = 'FileSource'
72
+
73
+ export type FileExportProps = Omit<ExportNode, 'kind'> & { key?: Key }
74
+
75
+ /**
76
+ * File.Export
77
+ *
78
+ * Declares an export entry for the current file. This will be collected by
79
+ * the FileCollector for later emission.
80
+ */
81
+ function FileExport(props: FileExportProps): KubbReactElement {
82
+ const { name, path, isTypeOnly, asAlias } = props
83
+
84
+ return <kubb-export name={name} path={path} isTypeOnly={isTypeOnly} asAlias={asAlias} />
85
+ }
86
+
87
+ FileExport.displayName = 'FileExport'
88
+
89
+ export type FileImportProps = Omit<ImportNode, 'kind'> & { key?: Key }
90
+
91
+ /**
92
+ * File.Import
93
+ *
94
+ * Declares an import entry for the current file.
95
+ */
96
+ function FileImport(props: FileImportProps): KubbReactElement {
97
+ const { name, root, path, isTypeOnly, isNameSpace } = props
98
+
99
+ return <kubb-import name={name} root={root} path={path} isNameSpace={isNameSpace} isTypeOnly={isTypeOnly} />
100
+ }
101
+
102
+ FileImport.displayName = 'FileImport'
103
+
104
+ File.Export = FileExport
105
+ File.Import = FileImport
106
+ File.Source = FileSource
@@ -0,0 +1,107 @@
1
+ import type { JSDoc, Key, KubbReactElement, KubbReactNode } from '../types.ts'
2
+
3
+ type Props = {
4
+ key?: Key
5
+ /**
6
+ * Name of the function.
7
+ */
8
+ name: string
9
+ /**
10
+ * Add default when export is being used
11
+ */
12
+ default?: boolean
13
+ /**
14
+ * Parameters/options/props that need to be used.
15
+ */
16
+ params?: string
17
+ /**
18
+ * Does this function need to be exported.
19
+ */
20
+ export?: boolean
21
+ /**
22
+ * Does the function has async/promise behavior.
23
+ * This will also add `Promise<returnType>` as the returnType.
24
+ */
25
+ async?: boolean
26
+ /**
27
+ * Generics that needs to be added for TypeScript.
28
+ */
29
+ generics?: string | string[]
30
+ /**
31
+ * ReturnType(see async for adding Promise type).
32
+ */
33
+ returnType?: string
34
+ /**
35
+ * Options for JSdocs.
36
+ */
37
+ JSDoc?: JSDoc
38
+ /**
39
+ * Children nodes.
40
+ */
41
+ children?: KubbReactNode
42
+ }
43
+
44
+ /**
45
+ * Generates a TypeScript function declaration.
46
+ */
47
+ export function Function({ children, ...props }: Props): KubbReactElement {
48
+ const { name, default: isDefault, export: canExport, async: isAsync, generics, params, returnType, JSDoc } = props
49
+
50
+ // Normalize generics array to comma-separated string for DOM attribute storage
51
+ const genericsString = Array.isArray(generics) ? generics.join(', ').trim() : generics
52
+
53
+ return (
54
+ <kubb-function
55
+ name={name}
56
+ params={params}
57
+ export={canExport}
58
+ default={isDefault}
59
+ async={isAsync}
60
+ generics={genericsString}
61
+ returnType={returnType}
62
+ JSDoc={JSDoc}
63
+ >
64
+ {children}
65
+ </kubb-function>
66
+ )
67
+ }
68
+
69
+ Function.displayName = 'Function'
70
+
71
+ type ArrowFunctionProps = Props & {
72
+ /**
73
+ * Create Arrow function in one line
74
+ */
75
+ singleLine?: boolean
76
+ }
77
+
78
+ /**
79
+ * ArrowFunction
80
+ *
81
+ * Renders an arrow function definition. Supports the same flags as `Function`.
82
+ * Use `singleLine` to render the body as a single-line expression.
83
+ */
84
+ function ArrowFunction({ children, ...props }: ArrowFunctionProps) {
85
+ const { name, default: isDefault, export: canExport, async, generics, params, returnType, JSDoc, singleLine } = props
86
+
87
+ const genericsString = Array.isArray(generics) ? generics.join(', ').trim() : generics
88
+
89
+ return (
90
+ <kubb-arrow-function
91
+ name={name}
92
+ params={params}
93
+ export={canExport}
94
+ default={isDefault}
95
+ async={async}
96
+ generics={genericsString}
97
+ returnType={returnType}
98
+ singleLine={singleLine}
99
+ JSDoc={JSDoc}
100
+ >
101
+ {children}
102
+ </kubb-arrow-function>
103
+ )
104
+ }
105
+
106
+ ArrowFunction.displayName = 'ArrowFunction'
107
+ Function.Arrow = ArrowFunction
@@ -0,0 +1,34 @@
1
+ import type { KubbReactElement } from '../types.ts'
2
+
3
+ type Props = {
4
+ /**
5
+ * Raw JSX string to embed verbatim in the generated code.
6
+ * Supports JSX fragments (`<>…</>`), elements, and any valid JSX syntax.
7
+ * @example
8
+ * ```tsx
9
+ * <Jsx>{'<>\n <a href={href}>Open</a>\n</>'}</Jsx>
10
+ * ```
11
+ */
12
+ children?: string
13
+ }
14
+
15
+ /**
16
+ * Embeds a raw JSX string verbatim in the generated source code.
17
+ *
18
+ * Use this component when you need to include JSX markup (including fragments
19
+ * `<>…</>`) in the body of a generated function or component. The `children`
20
+ * prop must be a plain string — expression attributes that reference runtime
21
+ * values should be written as template literals.
22
+ *
23
+ * @example
24
+ * ```tsx
25
+ * <Function name="MyComponent" export>
26
+ * <Jsx>{'return (\n <>\n <div>Hello</div>\n </>\n)'}</Jsx>
27
+ * </Function>
28
+ * ```
29
+ */
30
+ export function Jsx({ children }: Props): KubbReactElement {
31
+ return <kubb-jsx>{children}</kubb-jsx>
32
+ }
33
+
34
+ Jsx.displayName = 'Jsx'
@@ -0,0 +1,64 @@
1
+ import { Component } from 'react'
2
+ import type { KubbReactElement, KubbReactNode } from '../types.ts'
3
+
4
+ type ErrorBoundaryProps = {
5
+ onError: (error: Error) => void
6
+ children?: KubbReactNode
7
+ }
8
+
9
+ class ErrorBoundary extends Component<{
10
+ onError: ErrorBoundaryProps['onError']
11
+ children?: KubbReactNode
12
+ }> {
13
+ state = { hasError: false }
14
+
15
+ static displayName = 'ErrorBoundary'
16
+ static getDerivedStateFromError(_error: Error) {
17
+ return { hasError: true }
18
+ }
19
+
20
+ componentDidCatch(error: Error) {
21
+ if (error) {
22
+ this.props.onError(error)
23
+ }
24
+ }
25
+
26
+ render() {
27
+ if (this.state.hasError) {
28
+ return null
29
+ }
30
+ return this.props.children
31
+ }
32
+ }
33
+
34
+ export type RootProps = {
35
+ /**
36
+ * Exit (unmount) the whole app.
37
+ */
38
+ onExit: (error?: Error) => void
39
+ /**
40
+ * Error hook receiving runtime exceptions.
41
+ */
42
+ onError: (error: Error) => void
43
+ /**
44
+ * Children nodes.
45
+ */
46
+ children?: KubbReactNode
47
+ }
48
+
49
+ /**
50
+ * This component provides the root behavior for the Kubb runtime.
51
+ */
52
+ export function Root({ onError, children }: RootProps): KubbReactElement {
53
+ return (
54
+ <ErrorBoundary
55
+ onError={(error) => {
56
+ onError(error)
57
+ }}
58
+ >
59
+ {children}
60
+ </ErrorBoundary>
61
+ )
62
+ }
63
+
64
+ Root.displayName = 'Root'