@kubb/react-fabric 0.0.0-canary-20251020201500

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 (73) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +52 -0
  3. package/dist/devtools.cjs +10817 -0
  4. package/dist/devtools.cjs.map +1 -0
  5. package/dist/devtools.d.cts +1 -0
  6. package/dist/devtools.d.ts +1 -0
  7. package/dist/devtools.js +10817 -0
  8. package/dist/devtools.js.map +1 -0
  9. package/dist/globals-8sJ940pg.cjs +0 -0
  10. package/dist/globals-C6rGETh5.d.ts +166 -0
  11. package/dist/globals-CnATk-Sl.d.cts +166 -0
  12. package/dist/globals-Df5klKjG.js +1 -0
  13. package/dist/globals.cjs +1 -0
  14. package/dist/globals.d.cts +2 -0
  15. package/dist/globals.d.ts +2 -0
  16. package/dist/globals.js +3 -0
  17. package/dist/index.cjs +15924 -0
  18. package/dist/index.cjs.map +1 -0
  19. package/dist/index.d.cts +331 -0
  20. package/dist/index.d.ts +331 -0
  21. package/dist/index.js +15899 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/jsx-dev-runtime.cjs +9 -0
  24. package/dist/jsx-dev-runtime.d.cts +13 -0
  25. package/dist/jsx-dev-runtime.d.ts +13 -0
  26. package/dist/jsx-dev-runtime.js +6 -0
  27. package/dist/jsx-runtime-B3MMb3PL.cjs +233 -0
  28. package/dist/jsx-runtime-B3MMb3PL.cjs.map +1 -0
  29. package/dist/jsx-runtime-BPQkRAg2.js +228 -0
  30. package/dist/jsx-runtime-BPQkRAg2.js.map +1 -0
  31. package/dist/jsx-runtime-DmD5u6a-.js +13 -0
  32. package/dist/jsx-runtime-DmD5u6a-.js.map +1 -0
  33. package/dist/jsx-runtime-zKfRQHQD.cjs +36 -0
  34. package/dist/jsx-runtime-zKfRQHQD.cjs.map +1 -0
  35. package/dist/jsx-runtime.cjs +9 -0
  36. package/dist/jsx-runtime.js +6 -0
  37. package/dist/react-BBkwFtZV.js +1138 -0
  38. package/dist/react-BBkwFtZV.js.map +1 -0
  39. package/dist/react-Bq0UOw6S.cjs +1156 -0
  40. package/dist/react-Bq0UOw6S.cjs.map +1 -0
  41. package/dist/types-C3p0Ljxf.d.cts +85 -0
  42. package/dist/types-DEroxUW0.d.ts +85 -0
  43. package/dist/types.cjs +0 -0
  44. package/dist/types.d.cts +2 -0
  45. package/dist/types.d.ts +2 -0
  46. package/dist/types.js +1 -0
  47. package/package.json +115 -0
  48. package/src/ReactTemplate.tsx +229 -0
  49. package/src/components/App.tsx +27 -0
  50. package/src/components/Const.tsx +53 -0
  51. package/src/components/File.tsx +100 -0
  52. package/src/components/Function.tsx +139 -0
  53. package/src/components/Indent.tsx +53 -0
  54. package/src/components/Root.tsx +75 -0
  55. package/src/components/Type.tsx +40 -0
  56. package/src/createApp.ts +23 -0
  57. package/src/devtools.ts +118 -0
  58. package/src/dom.ts +75 -0
  59. package/src/globals.ts +52 -0
  60. package/src/hooks/useApp.ts +15 -0
  61. package/src/hooks/useFile.ts +14 -0
  62. package/src/hooks/useLifecycle.tsx +18 -0
  63. package/src/index.ts +24 -0
  64. package/src/jsx-runtime.ts +8 -0
  65. package/src/kubbRenderer.ts +175 -0
  66. package/src/types.ts +53 -0
  67. package/src/utils/createJSDoc.ts +9 -0
  68. package/src/utils/getFunctionParams.ts +236 -0
  69. package/src/utils/processFiles.ts +44 -0
  70. package/src/utils/squashExportNodes.ts +23 -0
  71. package/src/utils/squashImportNodes.ts +23 -0
  72. package/src/utils/squashSourceNodes.ts +40 -0
  73. package/src/utils/squashTextNodes.ts +82 -0
@@ -0,0 +1,229 @@
1
+ import process from 'node:process'
2
+ import type { AppContext } from '@kubb/fabric-core'
3
+ import type { ReactNode } from 'react'
4
+ import { ConcurrentRoot } from 'react-reconciler/constants'
5
+ import { onExit } from 'signal-exit'
6
+ import { Root } from './components/Root.tsx'
7
+ import { createNode } from './dom.ts'
8
+ import type { FiberRoot } from './kubbRenderer.ts'
9
+ import { KubbRenderer } from './kubbRenderer.ts'
10
+ import type { DOMElement } from './types.ts'
11
+ import { squashTextNodes } from './utils/squashTextNodes.ts'
12
+ import { processFiles } from './utils/processFiles.ts'
13
+
14
+ export type ReactTemplateOptions = {
15
+ context: AppContext
16
+ stdout?: NodeJS.WriteStream
17
+ stdin?: NodeJS.ReadStream
18
+ stderr?: NodeJS.WriteStream
19
+ /**
20
+ * Set this to true to always see the result of the render in the console(line per render)
21
+ */
22
+ debug?: boolean
23
+ }
24
+
25
+ export class ReactTemplate {
26
+ readonly #options: ReactTemplateOptions
27
+ // Ignore last render after unmounting a tree to prevent empty output before exit
28
+ #isUnmounted: boolean
29
+
30
+ exitPromise?: Promise<void>
31
+ readonly #container: FiberRoot
32
+ readonly #rootNode: DOMElement
33
+
34
+ constructor(options: ReactTemplateOptions) {
35
+ this.#options = { ...options }
36
+
37
+ this.#rootNode = createNode('kubb-root')
38
+ this.#rootNode.onRender = this.onRender
39
+ this.#rootNode.onImmediateRender = this.onRender
40
+
41
+ // Ignore last render after unmounting a tree to prevent empty output before exit
42
+ this.#isUnmounted = false
43
+ this.unmount.bind(this)
44
+
45
+ const originalError = console.error
46
+ console.error = (data: string | Error) => {
47
+ const message = typeof data === 'string' ? data : data?.message
48
+
49
+ if (message.match(/Encountered two children with the same key/gi)) {
50
+ return
51
+ }
52
+ if (message.match(/React will try to recreat/gi)) {
53
+ return
54
+ }
55
+ if (message.match(/Each child in a list should have a unique/gi)) {
56
+ return
57
+ }
58
+ if (message.match(/The above error occurred in the <KubbErrorBoundary/gi)) {
59
+ return
60
+ }
61
+
62
+ if (message.match(/A React Element from an older version of React was render/gi)) {
63
+ return
64
+ }
65
+
66
+ originalError(data)
67
+ }
68
+
69
+ // Report when an error was detected in a previous render
70
+ // https://github.com/pmndrs/react-three-fiber/pull/2261
71
+ const logRecoverableError =
72
+ typeof reportError === 'function'
73
+ ? // In modern browsers, reportError will dispatch an error event,
74
+ // emulating an uncaught JavaScript error.
75
+ reportError
76
+ : // In older browsers and test environments, fallback to console.error.
77
+ console.error
78
+
79
+ const rootTag = ConcurrentRoot
80
+ const hydrationCallbacks = null
81
+ const isStrictMode = false
82
+ const concurrentUpdatesByDefaultOverride = false
83
+ const identifierPrefix = 'id'
84
+ const onUncaughtError = logRecoverableError
85
+ const onCaughtError = logRecoverableError
86
+ const onRecoverableError = logRecoverableError
87
+ const transitionCallbacks = null
88
+
89
+ this.#container = KubbRenderer.createContainer(
90
+ this.#rootNode,
91
+ rootTag,
92
+ hydrationCallbacks,
93
+ isStrictMode,
94
+ concurrentUpdatesByDefaultOverride,
95
+ identifierPrefix,
96
+ onUncaughtError,
97
+ onCaughtError,
98
+ onRecoverableError,
99
+ transitionCallbacks,
100
+ )
101
+
102
+ // Unmount when process exits
103
+ this.unsubscribeExit = onExit(
104
+ (code) => {
105
+ this.unmount(code)
106
+ },
107
+ { alwaysLast: false },
108
+ ).bind(this)
109
+
110
+ KubbRenderer.injectIntoDevTools({
111
+ bundleType: 0, // 0 for PROD, 1 for DEV
112
+ version: '19.1.1', // should be React version and not Kubb's custom version
113
+ rendererPackageName: 'kubb', // package name
114
+ })
115
+ }
116
+ resolveExitPromise: () => void = () => {}
117
+ rejectExitPromise: (reason?: Error) => void = () => {}
118
+ unsubscribeExit: () => void = () => {}
119
+ onRender: () => void = async () => {
120
+ if (this.#isUnmounted) {
121
+ return
122
+ }
123
+
124
+ this.#options.context.clear()
125
+
126
+ processFiles(this.#rootNode, this.#options.context)
127
+
128
+ if (!this.#options.debug && !this.#options.stdout) {
129
+ return
130
+ }
131
+
132
+ const output = await this.#getOutput(this.#rootNode, this.#options.context)
133
+
134
+ if (this.#options.debug) {
135
+ console.log('Rendering: \n')
136
+ console.log(output)
137
+ }
138
+
139
+ if (this.#options.stdout) {
140
+ this.#options.stdout.clearLine(0)
141
+ this.#options.stdout.cursorTo(0)
142
+ this.#options.stdout.write(output)
143
+ }
144
+ }
145
+ onError(error: Error): void {
146
+ if (process.env.NODE_ENV === 'test') {
147
+ console.warn(error)
148
+ }
149
+
150
+ throw error
151
+ }
152
+ onExit(error?: Error): void {
153
+ this.unmount(error)
154
+ }
155
+
156
+ async #getOutput(node: DOMElement, context: AppContext): Promise<string> {
157
+ const text = squashTextNodes(node)
158
+ const files = context.files
159
+
160
+ return files.length
161
+ ? [...files]
162
+ .flatMap((file) => [...file.sources].map((item) => item.value))
163
+ .filter(Boolean)
164
+ .join('\n\n')
165
+ : text
166
+ }
167
+
168
+ render(node: ReactNode): void {
169
+ const element = (
170
+ <Root onExit={this.onExit.bind(this)} onError={this.onError.bind(this)}>
171
+ {node}
172
+ </Root>
173
+ )
174
+
175
+ KubbRenderer.updateContainerSync(element, this.#container, null, null)
176
+ KubbRenderer.flushSyncWork()
177
+ }
178
+
179
+ async renderToString(node: ReactNode): Promise<string> {
180
+ const element = (
181
+ <Root onExit={this.onExit.bind(this)} onError={this.onError.bind(this)}>
182
+ {node}
183
+ </Root>
184
+ )
185
+
186
+ KubbRenderer.updateContainerSync(element, this.#container, null, null)
187
+ KubbRenderer.flushSyncWork()
188
+
189
+ this.#options.context.clear()
190
+
191
+ return this.#getOutput(this.#rootNode, this.#options.context)
192
+ }
193
+
194
+ unmount(error?: Error | number | null): void {
195
+ if (this.#isUnmounted) {
196
+ return
197
+ }
198
+
199
+ if (this.#options.debug) {
200
+ console.log('Unmount', error)
201
+ }
202
+
203
+ this.onRender()
204
+ this.unsubscribeExit()
205
+
206
+ this.#isUnmounted = true
207
+
208
+ KubbRenderer.updateContainerSync(null, this.#container, null, null)
209
+
210
+ if (error instanceof Error) {
211
+ this.rejectExitPromise(error)
212
+
213
+ return
214
+ }
215
+
216
+ this.resolveExitPromise()
217
+ }
218
+
219
+ async waitUntilExit(): Promise<void> {
220
+ if (!this.exitPromise) {
221
+ this.exitPromise = new Promise((resolve, reject) => {
222
+ this.resolveExitPromise = resolve
223
+ this.rejectExitPromise = reject
224
+ })
225
+ }
226
+
227
+ return this.exitPromise
228
+ }
229
+ }
@@ -0,0 +1,27 @@
1
+ import { createContext, useContext } from 'react'
2
+ import type { KubbNode } from '../types.ts'
3
+ import { RootContext } from './Root.tsx'
4
+
5
+ export type AppContextProps<TMeta = unknown> = {
6
+ /**
7
+ * Exit (unmount)
8
+ */
9
+ readonly exit: (error?: Error) => void
10
+ readonly meta: TMeta
11
+ }
12
+
13
+ const AppContext = createContext<AppContextProps | undefined>(undefined)
14
+
15
+ type Props<TMeta = unknown> = {
16
+ readonly children?: KubbNode
17
+ readonly meta: TMeta
18
+ }
19
+
20
+ export function App<TMeta = unknown>({ meta, children }: Props<TMeta>) {
21
+ const { exit } = useContext(RootContext)
22
+
23
+ return <AppContext.Provider value={{ exit, meta }}>{children}</AppContext.Provider>
24
+ }
25
+
26
+ App.Context = AppContext
27
+ App.displayName = 'KubbApp'
@@ -0,0 +1,53 @@
1
+ import type { JSDoc, Key, KubbNode } from '../types.ts'
2
+
3
+ import { createJSDoc } from '../utils/createJSDoc.ts'
4
+
5
+ type Props = {
6
+ key?: Key
7
+ /**
8
+ * Name of the const
9
+ */
10
+ name: string
11
+ /**
12
+ * Does this type need to be exported.
13
+ */
14
+ export?: boolean
15
+ /**
16
+ * Type to make the const being typed
17
+ */
18
+ type?: string
19
+ /**
20
+ * Options for JSdocs.
21
+ */
22
+ JSDoc?: JSDoc
23
+ /**
24
+ * Use of `const` assertions
25
+ */
26
+ asConst?: boolean
27
+ children?: KubbNode
28
+ }
29
+
30
+ export function Const({ name, export: canExport, type, JSDoc, asConst, children }: Props) {
31
+ return (
32
+ <>
33
+ {JSDoc?.comments && (
34
+ <>
35
+ {createJSDoc({ comments: JSDoc?.comments })}
36
+ <br />
37
+ </>
38
+ )}
39
+ {canExport && <>export </>}
40
+ const {name}{' '}
41
+ {type && (
42
+ <>
43
+ {':'}
44
+ {type}{' '}
45
+ </>
46
+ )}
47
+ = {children}
48
+ {asConst && <> as const</>}
49
+ </>
50
+ )
51
+ }
52
+
53
+ Const.displayName = 'KubbConst'
@@ -0,0 +1,100 @@
1
+ import { createContext } from 'react'
2
+
3
+ import type { KubbFile } from '@kubb/fabric-core'
4
+ import type { Key, KubbNode } from '../types.ts'
5
+
6
+ export type FileContextProps<TMeta extends object = object> = {
7
+ /**
8
+ * Name to be used to dynamicly create the baseName(based on input.path).
9
+ * Based on UNIX basename
10
+ * @link https://nodejs.org/api/path.html#pathbasenamepath-suffix
11
+ */
12
+ baseName: KubbFile.BaseName
13
+ /**
14
+ * Path will be full qualified path to a specified file.
15
+ */
16
+ path: KubbFile.Path
17
+ meta?: TMeta
18
+ }
19
+ const FileContext = createContext<FileContextProps>({} as FileContextProps)
20
+
21
+ type BasePropsWithBaseName = {
22
+ /**
23
+ * Name to be used to dynamicly create the baseName(based on input.path).
24
+ * Based on UNIX basename
25
+ * @link https://nodejs.org/api/path.html#pathbasenamepath-suffix
26
+ */
27
+ baseName: KubbFile.BaseName
28
+ /**
29
+ * Path will be full qualified path to a specified file.
30
+ */
31
+ path: KubbFile.Path
32
+ }
33
+
34
+ type BasePropsWithoutBaseName = {
35
+ baseName?: never
36
+ /**
37
+ * Path will be full qualified path to a specified file.
38
+ */
39
+ path?: KubbFile.Path
40
+ }
41
+
42
+ type BaseProps = BasePropsWithBaseName | BasePropsWithoutBaseName
43
+
44
+ type Props<TMeta> = BaseProps & {
45
+ key?: Key
46
+ meta?: TMeta
47
+ banner?: string
48
+ footer?: string
49
+ children?: KubbNode
50
+ }
51
+
52
+ export function File<TMeta extends object = object>({ children, ...rest }: Props<TMeta>) {
53
+ if (!rest.baseName || !rest.path) {
54
+ return <>{children}</>
55
+ }
56
+
57
+ return (
58
+ <kubb-file {...rest}>
59
+ <FileContext.Provider value={{ baseName: rest.baseName, path: rest.path, meta: rest.meta }}>{children}</FileContext.Provider>
60
+ </kubb-file>
61
+ )
62
+ }
63
+
64
+ File.displayName = 'KubbFile'
65
+
66
+ type FileSourceProps = Omit<KubbFile.Source, 'value'> & {
67
+ key?: Key
68
+ children?: KubbNode
69
+ }
70
+
71
+ function FileSource({ isTypeOnly, name, isExportable, isIndexable, children }: FileSourceProps) {
72
+ return (
73
+ <kubb-source name={name} isTypeOnly={isTypeOnly} isExportable={isExportable} isIndexable={isIndexable}>
74
+ {children}
75
+ </kubb-source>
76
+ )
77
+ }
78
+
79
+ FileSource.displayName = 'KubbFileSource'
80
+
81
+ type FileExportProps = KubbFile.Export & { key?: Key }
82
+
83
+ function FileExport({ name, path, isTypeOnly, asAlias }: FileExportProps) {
84
+ return <kubb-export name={name} path={path} isTypeOnly={isTypeOnly || false} asAlias={asAlias} />
85
+ }
86
+
87
+ FileExport.displayName = 'KubbFileExport'
88
+
89
+ type FileImportProps = KubbFile.Import & { key?: Key }
90
+
91
+ function FileImport({ name, root, path, isTypeOnly, isNameSpace }: FileImportProps) {
92
+ return <kubb-import name={name} root={root} path={path} isNameSpace={isNameSpace} isTypeOnly={isTypeOnly || false} />
93
+ }
94
+
95
+ FileImport.displayName = 'KubbFileImport'
96
+
97
+ File.Export = FileExport
98
+ File.Import = FileImport
99
+ File.Source = FileSource
100
+ File.Context = FileContext
@@ -0,0 +1,139 @@
1
+ import type { JSDoc, Key, KubbNode } from '../types.ts'
2
+ import { Indent } from './Indent.tsx'
3
+ import { createJSDoc } from '../utils/createJSDoc.ts'
4
+
5
+ type Props = {
6
+ key?: Key
7
+ /**
8
+ * Name of the function.
9
+ */
10
+ name: string
11
+ /**
12
+ * Add default when export is being used
13
+ */
14
+ default?: boolean
15
+ /**
16
+ * Parameters/options/props that need to be used.
17
+ */
18
+ params?: string
19
+ /**
20
+ * Does this function need to be exported.
21
+ */
22
+ export?: boolean
23
+ /**
24
+ * Does the function has async/promise behaviour.
25
+ * This will also add `Promise<returnType>` as the returnType.
26
+ */
27
+ async?: boolean
28
+ /**
29
+ * Generics that needs to be added for TypeScript.
30
+ */
31
+ generics?: string | string[]
32
+
33
+ /**
34
+ * ReturnType(see async for adding Promise type).
35
+ */
36
+ returnType?: string
37
+ /**
38
+ * Options for JSdocs.
39
+ */
40
+ JSDoc?: JSDoc
41
+ children?: KubbNode
42
+ }
43
+
44
+ export function Function({ name, default: isDefault, export: canExport, async, generics, params, returnType, JSDoc, children }: Props) {
45
+ return (
46
+ <>
47
+ {JSDoc?.comments && (
48
+ <>
49
+ {createJSDoc({ comments: JSDoc?.comments })}
50
+ <br />
51
+ </>
52
+ )}
53
+ {canExport && <>export </>}
54
+ {isDefault && <>default </>}
55
+ {async && <>async </>}
56
+ function {name}
57
+ {generics && (
58
+ <>
59
+ {'<'}
60
+ {Array.isArray(generics) ? generics.join(', ').trim() : generics}
61
+ {'>'}
62
+ </>
63
+ )}
64
+ ({params}){returnType && !async && <>: {returnType}</>}
65
+ {returnType && async && (
66
+ <>
67
+ : Promise{'<'}
68
+ {returnType}
69
+ {'>'}
70
+ </>
71
+ )}
72
+ {' {'}
73
+ <br />
74
+ <Indent size={2}>{children}</Indent>
75
+ <br />
76
+ {'}'}
77
+ </>
78
+ )
79
+ }
80
+
81
+ Function.displayName = 'KubbFunction'
82
+
83
+ type ArrowFunctionProps = Props & {
84
+ /**
85
+ * Create Arrow function in one line
86
+ */
87
+ singleLine?: boolean
88
+ }
89
+
90
+ function ArrowFunction({ name, default: isDefault, export: canExport, async, generics, params, returnType, JSDoc, singleLine, children }: ArrowFunctionProps) {
91
+ return (
92
+ <>
93
+ {JSDoc?.comments && (
94
+ <>
95
+ {createJSDoc({ comments: JSDoc?.comments })}
96
+ <br />
97
+ </>
98
+ )}
99
+ {canExport && <>export </>}
100
+ {isDefault && <>default </>}
101
+ const {name} = {async && <>async </>}
102
+ {generics && (
103
+ <>
104
+ {'<'}
105
+ {Array.isArray(generics) ? generics.join(', ').trim() : generics}
106
+ {'>'}
107
+ </>
108
+ )}
109
+ ({params}){returnType && !async && <>: {returnType}</>}
110
+ {returnType && async && (
111
+ <>
112
+ : Promise{'<'}
113
+ {returnType}
114
+ {'>'}
115
+ </>
116
+ )}
117
+ {singleLine && (
118
+ <>
119
+ {' => '}
120
+ {children}
121
+ <br />
122
+ </>
123
+ )}
124
+ {!singleLine && (
125
+ <>
126
+ {' => {'}
127
+ <br />
128
+ <Indent size={2}>{children}</Indent>
129
+ <br />
130
+ {'}'}
131
+ <br />
132
+ </>
133
+ )}
134
+ </>
135
+ )
136
+ }
137
+
138
+ ArrowFunction.displayName = 'KubbArrowFunction'
139
+ Function.Arrow = ArrowFunction
@@ -0,0 +1,53 @@
1
+ import dedent from 'dedent'
2
+ import indentString from 'indent-string'
3
+ import React from 'react'
4
+
5
+ type IndentProps = {
6
+ size?: number
7
+ children?: React.ReactNode
8
+ }
9
+
10
+ /**
11
+ * Indents all children by `size` spaces.
12
+ * Collapses consecutive <br /> tags to at most 2.
13
+ */
14
+ export function Indent({ size = 2, children }: IndentProps) {
15
+ if (!children) return null
16
+
17
+ const filtered = React.Children.toArray(children).filter(Boolean)
18
+ const result: React.ReactNode[] = []
19
+
20
+ let prevWasBr = false
21
+ let brCount = 0
22
+
23
+ filtered.forEach((child) => {
24
+ if (React.isValidElement(child) && child.type === 'br') {
25
+ if (!prevWasBr || brCount < 2) {
26
+ result.push(child)
27
+ brCount++
28
+ }
29
+ prevWasBr = true
30
+ } else {
31
+ prevWasBr = false
32
+ brCount = 0
33
+ result.push(child)
34
+ }
35
+ })
36
+
37
+ return (
38
+ <>
39
+ {result.map((child) => {
40
+ if (typeof child === 'string') {
41
+ const cleaned = dedent(child)
42
+ return <>{indentString(cleaned, size)}</>
43
+ }
44
+ return (
45
+ <>
46
+ {' '.repeat(size)}
47
+ {child}
48
+ </>
49
+ )
50
+ })}
51
+ </>
52
+ )
53
+ }
@@ -0,0 +1,75 @@
1
+ import { Component, createContext } from 'react'
2
+
3
+ import type { KubbNode } from '../types.ts'
4
+
5
+ type ErrorBoundaryProps = {
6
+ onError: (error: Error) => void
7
+ children?: KubbNode
8
+ }
9
+
10
+ class ErrorBoundary extends Component<{
11
+ onError: ErrorBoundaryProps['onError']
12
+ children?: KubbNode
13
+ }> {
14
+ state = { hasError: false }
15
+
16
+ static displayName = 'KubbErrorBoundary'
17
+ static getDerivedStateFromError(_error: Error) {
18
+ return { hasError: true }
19
+ }
20
+
21
+ componentDidCatch(error: Error) {
22
+ if (error) {
23
+ this.props.onError(error)
24
+ }
25
+ }
26
+
27
+ render() {
28
+ if (this.state.hasError) {
29
+ return null
30
+ }
31
+ return this.props.children
32
+ }
33
+ }
34
+
35
+ export type RootContextProps = {
36
+ /**
37
+ * Exit (unmount) the whole Ink app.
38
+ */
39
+ readonly exit: (error?: Error) => void
40
+ }
41
+
42
+ export const RootContext = createContext<RootContextProps>({
43
+ exit: () => {},
44
+ })
45
+
46
+ type RootProps = {
47
+ /**
48
+ * Exit (unmount) hook
49
+ */
50
+ readonly onExit: (error?: Error) => void
51
+ /**
52
+ * Error hook
53
+ */
54
+ readonly onError: (error: Error) => void
55
+ readonly children?: KubbNode
56
+ }
57
+
58
+ export function Root({ onError, onExit, children }: RootProps) {
59
+ try {
60
+ return (
61
+ <ErrorBoundary
62
+ onError={(error) => {
63
+ onError(error)
64
+ }}
65
+ >
66
+ <RootContext.Provider value={{ exit: onExit }}>{children}</RootContext.Provider>
67
+ </ErrorBoundary>
68
+ )
69
+ } catch (_e) {
70
+ return null
71
+ }
72
+ }
73
+
74
+ Root.Context = RootContext
75
+ Root.displayName = 'KubbRoot'